<?php
/**
 * @file  multiotp.cli.header.php
 * @brief Command line implementation of the multiOTP PHP class.
 *
 * multiOTP PHP CLI header - Strong two-factor authentication PHP class
 * https://www.multiotp.net
 *
 * Visit http://forum.multiotp.net/ for additional support.
 *
 * Donation are always welcome! Please check https://www.multiOTP.net
 * and you will find the magic button ;-)
 *
 * If the name of this file is multiotp.php, it means that it is already
 * the result of the merge of the two files multiotp.cli.header.php and
 * multiotp.class.php
 *
 * The MultiOTP PHP CLI header is simply merged with the MultiOTP PHP
 * class in order to provide an authentication command line script.
 *
 * This script can be used as an external authentication provider with at
 * least the following RADIUS servers:
 *  - TekRADIUS LT, a free Radius server for Windows with SQLite backend
 *    (http:/www.tekradius.com)
 *  - TekRADIUS, a free Radius server for Windows with MS-SQL backend
 *    (http:/www.tekradius.com)
 *  - FreeRADIUS, a free Radius server implementation for Linux
 *    and *nix environments (http://freeradius.org)
 *  - WinRADIUS, the FreeRADIUS implementation ported for Windows
 *    (http://winradius.eu/)
 *
 * For Windows, you can also use the multiotp.exe file provided, which is
 * an embedded PHP interpreter together with the result of the merge.
 *
 * PHP 5.4.0 or higher is supported.
 *
 * @author    Andre Liechti, SysCo systemes de communication sa, <info@multiotp.net>
 * @version   5.10.0.4
 * @date      2025-11-04
 * @since     2010-06-08
 * @copyright (c) 2010-2025 SysCo systemes de communication sa
 * @copyright GNU Lesser General Public License
 *
 *//*
 *
 * LICENCE
 *
 *   Copyright (c) 2010-2025 SysCo systemes de communication sa
 *   SysCo (tm) is a trademark of SysCo systemes de communication sa
 *   (http://www.sysco.ch)
 *   All rights reserved.
 * 
 *   This file is part of the MultiOTP PHP class
 *
 *   MultiOTP PHP class is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Lesser General Public License as
 *   published by the Free Software Foundation, either version 3 of the License,
 *   or (at your option) any later version.
 * 
 *   MultiOTP PHP class 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 Lesser General Public License for more details.
 * 
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with MultiOTP PHP class.
 *   If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Command line usage
 *
 *   Type multiotp -help to have the full description of the options,
 *    and have a look at the readme.txt file for enhanced explanations
 *
 *
 * Return codes: see multiOTP class
 *
 *
 * Radius integration examples
 *
 *   Example 1 (FreeRADIUS 2.x under Linux or Windows, new fashion)
 *
 *     Have a look in the readme file, everything is explained.
 *
 *
 *   Example 2 (FreeRADIUS under Linux or Windows, old fashion)
 *
 *     Define a DEFAULT entry in the /etc/freeradius/users file like this:
 *     DEFAULT Auth-Type = Accept
 *     Exec-Program-Wait = "/usr/local/bin/multiotp.php %{User-Name} %{User-Password}",
 *     Fall-Through = Yes,
 *     Reply-Message = "Hello, %{User-Name}"
 *
 *
 *   Example 3 (TekRADIUS or TekRADIUS LT under Windows)
 *
 *     TekRADIUS supports a Default Username to be used when a matching user
 *     profile cannot be found for an incoming RADIUS authentication request.
 *     So a quick and easy way is to create in the TekRADIUS Manager a User
 *     named 'Default' that belongs to the existing 'Default' Group.
 *     Then add to this Default user the following attribute :
 *     Check  External-Executable  C:\multitop\multiotp.exe %ietf|1% %ietf|2%
 *
 *
 * External files created
 *
 *   Users database files in the subfolder called users
 *   Tokens database files in the subfolder called tokens
 *
 *
 * External file needed
 *
 *   Users database files in the subfolder called users
 *   Tokens database files in the subfolder called tokens
 *
 *
 * Special issues
 *
 *   If you need specific developements concerning strong authentication,
 *   do not hesistate to contact us per email at info@multiotp.net.
 *
 *
 * Change Log
 *
 *   Please check the readme file for the whole change log since 2010
 *********************************************************************/

global $argc;
global $argv;

if (!isset($multiotp)) {
	  require_once('multiotp.class.php');
}

// Trick to define the current folder as the script folder
function get_script_dir()
{
    // Detect the current folder, change Windows notation to universal notation if needed
    $current_folder = convert_to_unix_path(getcwd());
    $current_script_folder = convert_to_unix_path(isset($_SERVER["argv"][0])?$_SERVER["argv"][0]:'');
    if ('' == (nullable_trim($current_script_folder))) {
        if (isset($_SERVER['SCRIPT_FILENAME'])) {
            $current_script_folder = $_SERVER['SCRIPT_FILENAME'];
        } elseif (isset($argv[0])) {
            $current_script_folder = dirname($current_folder."/".$argv[0]);
        }
    }
    
    if (false === mb_strpos($current_script_folder,"/")) {
        $current_script_folder_detected = dirname($current_folder."/fake.file");
    } else {
        $current_script_folder_detected = dirname($current_script_folder);
    }

    if (mb_substr($current_script_folder_detected,-1) != "/") {
        $current_script_folder_detected.="/";
    }
    return convert_to_windows_path_if_needed($current_script_folder_detected);
}


// Function to convert into a unix path notation
function convert_to_unix_path(
    $path
) {
    return str_replace("\\","/",$path);
}


// Function to convert into a windows path notation if needed
function convert_to_windows_path_if_needed(
    $path
) {
    $result = $path;
    if (false !== mb_strpos($result,":")) {
        $result = str_replace("/","\\",$result);
    }
    return $result;
}


// Clean quotes of the parameters if any
if (!function_exists('clean_quotes')) {
    function clean_quotes(
        $value
    ) {
        $cleaned = FALSE;
        $var = $value;
        if ((1 < mb_strlen($var)) && ((('"' == mb_substr($var,0,1)) && ('"' == mb_substr($var,-1))) || (("'" == mb_substr($var,0,1)) && ("'" == mb_substr($var,-1))))) {
            $var = mb_substr($var, 1, mb_strlen($var)-2);
            $cleaned = TRUE;
        }
        if ($cleaned) {
          $var = clean_quotes($var);
        }
        return $var;
    }
}


// CLI mode initialization (if not, it's the http local proxy mode)
$cli_mode = TRUE;


// Local proxy mode (non-CLI mode) detection and adaptation
if ('127.0.0.1'==(isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:'')) {
    if (isset($_POST['argv']) || isset($_GET['argv'])) {
        $cli_mode = FALSE;
        $folder_path = dirname(__FILE__).'/';
        if (!@file_exists($folder_path.'scripts/')) {
          $folder_path = '/usr/local/bin/multiotp/';
        }
        if (!@file_exists($folder_path.'templates/')) {
            $folder_path = '';
        } else {
            $chdir_result = chdir($folder_path);
        }
        $detected_folder_path = $folder_path;
        ob_start();
    }
}

if ($cli_mode) {
    // We try to detect the current folder where multiOTP is installed
    $folder_path = get_script_dir();
    $chdir_result = chdir($folder_path);
    $detected_folder_path = $folder_path;

    // Trick to have mostly the correct timezone in embedded command line version
    // and to avoid error messages when using time functions
    if (function_exists("date_default_timezone_get"))
    {
        $actual_timezone = @date_default_timezone_get();
        if (function_exists("date_default_timezone_set"))
        {
            @date_default_timezone_set($actual_timezone);
        }
    }

    // Be sure that STDIN, STDOUT and STDERR are defined correctly for command line edition
    if (!constant_defined('STDIN')) {
        define('STDIN', @fopen('php://stdin', 'r'));
    }
    if (!constant_defined('STDOUT')) {
        define('STDOUT', @fopen('php://stdout', 'w'));
    }
    if (!constant_defined('STDERR')) {
        define('STDERR', @fopen('php://stderr', 'w'));
    }
}


// Initialize some variables
$command             = "";
$not_a_command       = false;
$command_array       = array();
$call_method         = "";
$cp_mode             = false;
$display_help        = false;
$display_status      = false;
$prefix_pin          = false;
$crlf                = "\n"; // was chr(13).chr(10);
$result              = _MULTIOTP_ERROR_; // Unknown error
$token_id_creation   = false;
$mysql_parameters    = array();
$pgsql_parameters    = array();
$no_php_info         = false;
$param_info_debug    = false;
$show_false_pin      = false;
$base_dir            = '';
$source_tag          = '';
$source_ip           = '';
$source_mac          = '';
$calling_ip          = '';
$calling_mac         = '';
$chap_id             = '';
$chap_challenge      = '';
$chap_password       = '';
$ms_chap_challenge   = '';
$ms_chap_response    = '';
$ms_chap2_response   = '';
$verbose_prefix      = '';
$display_log         = false;
$enable_log          = false;
$verbose_log         = false;
$initialize_backend  = false;
$keep_local          = false;
$encrypted_password  = false;
$request_nt_key      = false;
$server_cache_level  = '';
$server_secret       = '';
$server_timeout      = '';
$server_url          = '';
$state               = '';
$sync_delete_retention_days = '';
$user_sid            = '';
$write_config_data   = false;
$write_param_data    = false;
$nt_key_only         = false;


// Extract all parameters
$param_count = 0;
$all_args = array();
$all_args_size = 20;

if ($cli_mode) {
    $loop_start = 1;
    $argv = isset($_SERVER["argv"]) ? $_SERVER["argv"] : (isset($argv) ? $argv : "");
    $argc = intval(isset($_SERVER["argc"]) ? $_SERVER["argc"] : (isset($argc) ? $argc : 0));
} else {
    $argv = array();
    $loop_start = 1;
    if (isset($_POST['argv']) || isset($_GET['argv'])) {
        $argv[] = __FILE__;
        $all_argv = explode(chr(0), base64_decode(isset($_POST['argv'])?$_POST['argv']:$_GET['argv']));
        foreach ($all_argv as $one_argv) {
            if ('' != nullable_trim($one_argv)) {
                $argv[] = nullable_trim($one_argv);
            }
        }
    }
    $argc = count($argv);
}


for ($arg_loop=$loop_start; $arg_loop < $argc; $arg_loop++) {

    $current_arg = encode_utf8_if_needed(clean_quotes($argv[$arg_loop]));
    
    $not_a_command = FALSE;

    if ("-activate" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "activate";
    } elseif ("-assign-token" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "assign-token";
    } elseif ("-callapi" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "callapi";
    } elseif ("-backup-config" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "backup-config";
    } elseif ("-call-method=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,13)) {
        $command = "call-method";
        $src_array = explode("=",$current_arg,2);
        if (2 == count($src_array)) {
            $call_method = clean_quotes($src_array[1]);
        }
    } elseif ("-check" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "check";
    } elseif ("-iswithout2fa" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "iswithout2fa";
    } elseif ("-check-ldap-password" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "check-ldap-password";
    } elseif ("-checkpam" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "checkpam";
    } elseif ("-clearlog" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "clearlog";
    } elseif ("-config" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "config";
    } elseif ("-create" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "create";
    } elseif ("-createga" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "createga";
    } elseif ("-customfunction" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "customfunction";
    } elseif ("-custominfo" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "custominfo";
    } elseif ("-default-dialin-ip-mask" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "default-dialin-ip-mask";
    } elseif ("-delete" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "delete";
    } elseif ("-delete-token" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "delete-token";
    } elseif ("-deactivate" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "deactivate";
    } elseif ("-desactivate" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "desactivate";
    } elseif ("-dialin-ip-address" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "dialin-ip-address";
    } elseif ("-dialin-ip-mask" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "dialin-ip-mask";
    } elseif ("-fastcreate" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "fastcreate";
    } elseif ("-fastcreatenopin" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "fastcreatenopin";
    } elseif ("-fastcreatewithpin" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "fastcreatewithpin";
    } elseif ("-hardware" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "hardware";
    } elseif ("-help" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "help";
    } elseif ("-import" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "import";
    } elseif ("-import-alpine-xml" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "import-alpine-xml";
    } elseif ("-import-csv" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "import-csv";
    } elseif ("-import-dat" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "import-dat";
    } elseif ("-import-pskc" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "import-pskc";
    } elseif ("-import-sql" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "import-sql";
    } elseif ("-import-xml" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "import-xml";
    } elseif ("-import-yubikey" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "import-yubikey";
    } elseif ("-initialize-backend" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "initialize-backend";
        $initialize_backend = true;
    } elseif ("-lockeduserslist" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "lockeduserslist";
    } elseif ("-ldap-users-list" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "ldap-users-list";
    } elseif ("-ldap-users-sync" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "ldap-users-sync";
    } elseif ("-ldap-user-info" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "ldap-user-info";
    } elseif ("-ldap-check" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "ldap-check";
    } elseif ("-phpinfo" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "phpinfo";
    } elseif ("-libhash" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "libhash";
    } elseif ("-lock" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "lock";
    } elseif ("-mysql" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "mysql";
    } elseif ("-pgsql" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "pgsql";
    } elseif ("-php-debug-on-screen" == mb_strtolower($current_arg,'UTF-8')) {
        ini_set("error_reporting", -1);
        ini_set("display_errors", "On");
    } elseif ("-php-version" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "php-version";
    } elseif ("-purge-lock-folder" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "purge-lock-folder";
    } elseif ("-purge-ldap-cache-folder" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "purge-ldap-cache-folder";
    } elseif ("-qrcode" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "qrcode";
    } elseif ("-qrcodes" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "qrcodes";
    } elseif ("-htmlinfo" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "htmlinfo";
    } elseif ("-requiresms" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "requiresms";
    } elseif ("-remove-token" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "remove-token";
    } elseif ("-restore-config" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "restore-config";
    } elseif ("-resync" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "resync";
    } elseif ("-scratchlist" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "scratchlist";
    } elseif ("-seed-info" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "seed";
    } elseif ("-set" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "set";
    } elseif ("-showlog" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "showlog";
    } elseif ("-error-codes" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "error-codes";
    } elseif ("-tokenslist" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "tokenslist";
    } elseif ("-unlock" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "unlock";
    } elseif ("-update" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "update";
    } elseif ("-update-pin" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "update-pin";
    } elseif ("-update-windows-passwords" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "update-windows-passwords";
    } elseif ("-urllink" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "urllink";
    } elseif ("-user-info" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "user-info";
    } elseif ("-userslist" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "userslist";
    } elseif (("-version" == mb_strtolower($current_arg,'UTF-8')) || ("-v" == mb_strtolower($current_arg,'UTF-8'))) {
        $command = "version";
    } elseif ("-version-only" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "version-only";
    } elseif ("-log-error" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "log-error";
    } elseif ("-log-info" == mb_strtolower($current_arg,'UTF-8')) {
        $command = "log-info";
    } else {
        // The current argument is not a command
        $not_a_command = TRUE;
        if ("-base-dir=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,10)) {
            $base_array = explode("=",$current_arg,2);
            if (2 == count($base_array)) {
                $base_dir = clean_quotes($base_array[1]);
            }
        } elseif ("-src=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,5)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $source_ip = clean_quotes($src_array[1]);
            }
        } elseif ("-tag=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,5)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $source_tag = clean_quotes($src_array[1]);
            }
        } elseif ("-mac=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,5)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $source_mac = clean_quotes($src_array[1]);
            }
        } elseif ("-calling-ip=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,12)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $calling_ip = clean_quotes($src_array[1]);
            }
        } elseif ("-calling-mac=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,13)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $calling_mac = clean_quotes($src_array[1]);
            }
        } elseif ("-chap-id=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,16)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $chap_id = clean_quotes($src_array[1]);
                if (("%msoft" == mb_strtolower(mb_substr($chap_id,0,6),'UTF-8')) || ("%ietf" == mb_strtolower(mb_substr($chap_id,0,5),'UTF-8'))) {
                    $chap_id = '';
                }
            }
        } elseif ("-chap-challenge=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,16)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $chap_challenge = clean_quotes($src_array[1]);
                if (("%msoft" == mb_strtolower(mb_substr($chap_challenge,0,6),'UTF-8')) || ("%ietf" == mb_strtolower(mb_substr($chap_challenge,0,5),'UTF-8'))) {
                    $chap_challenge = '';
                }
            }
        } elseif ("-chap-password=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,15)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $chap_password = clean_quotes($src_array[1]);
                if (("%msoft" == mb_strtolower(mb_substr($chap_password,0,6),'UTF-8')) || ("%ietf" == mb_strtolower(mb_substr($chap_password,0,5),'UTF-8'))) {
                    $chap_password = '';
                } else {
                    $encrypted_password = true;
                }
            }
        } elseif ("-ms-chap-challenge=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,19)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $ms_chap_challenge = clean_quotes($src_array[1]);
                if (("%msoft" == mb_strtolower(mb_substr($ms_chap_challenge,0,6),'UTF-8')) || ("%ietf" == mb_strtolower(mb_substr($ms_chap_challenge,0,5),'UTF-8'))) {
                    $ms_chap_challenge = '';
                }
            }
        } elseif ("-ms-chap-response=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,18)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $ms_chap_response = clean_quotes($src_array[1]);
                if (("%msoft" == mb_strtolower(mb_substr($ms_chap_response,0,6),'UTF-8')) || ("%ietf" == mb_strtolower(mb_substr($ms_chap_response,0,5),'UTF-8'))) {
                    $ms_chap_response = '';
                } else {
                    $encrypted_password = true;
                }
            }
        } elseif ("-ms-chap2-response=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,19)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $ms_chap2_response = clean_quotes($src_array[1]);
                if (("%msoft" == mb_strtolower(mb_substr($ms_chap2_response,0,6),'UTF-8')) || ("%ietf" == mb_strtolower(mb_substr($ms_chap2_response,0,5),'UTF-8'))) {
                    $ms_chap2_response = '';
                } else {
                    $encrypted_password = true;
                }
            }
        } elseif ("-server-url=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,12)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $server_url = nullable_trim(str_replace(",",";",str_replace(" ",";",clean_quotes($src_array[1]))));
            }
        } elseif ("-server-cache-level=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,20)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $server_cache_level = clean_quotes($src_array[1]);
            }
        } elseif ("-server-secret=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,15)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $server_secret = clean_quotes($src_array[1]);
            }
        } elseif ("-server-timeout=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,16)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $server_timeout = clean_quotes($src_array[1]);
            }
        } elseif ("-state=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,7)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $state = clean_quotes($src_array[1]);
            }
        } elseif ("-sync-delete-retention-days=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,28)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $sync_delete_retention_days = clean_quotes($src_array[1]);
            }
        } elseif ("-usersid=" == mb_substr(mb_strtolower($current_arg,'UTF-8'),0,9)) {
            $src_array = explode("=",$current_arg,2);
            if (2 == count($src_array)) {
                $user_sid = clean_quotes($src_array[1]);
            }
        } elseif ("-cp" == mb_strtolower($current_arg,'UTF-8')) {
            $cp_mode = true;
        } elseif ("-debug" == mb_strtolower($current_arg,'UTF-8')) {
            $verbose_log = true;
        } elseif ("-display-log" == mb_strtolower($current_arg,'UTF-8')) {
            $display_log = true;
        } elseif ("-log" == mb_strtolower($current_arg,'UTF-8')) {
            $enable_log = true;
        } elseif ("-keep-local" == mb_strtolower($current_arg,'UTF-8')) {
            $keep_local = true;
        } elseif ("-no-php-info" == mb_strtolower($current_arg,'UTF-8')) {
            $no_php_info = true;
        } elseif ("-no-prefix-pin" == mb_strtolower($current_arg,'UTF-8')) {
            $set_prefix_pin = false;
        } elseif ("-nt-key-only" == mb_strtolower($current_arg,'UTF-8')) {
            $nt_key_only = true;
        } elseif ("-param" == mb_strtolower($current_arg,'UTF-8')) {
            $param_info_debug = true;
        } elseif ("-prefix-pin" == mb_strtolower($current_arg,'UTF-8')) {
            $set_prefix_pin = true;
        } elseif (("-request-nt-key" == mb_strtolower($current_arg,'UTF-8')) || ("--request-nt-key" == mb_strtolower($current_arg,'UTF-8'))) {
            $request_nt_key = true;
        } elseif ("-show-false-pin" == mb_strtolower($current_arg,'UTF-8')) {
            $show_false_pin = true;
        } elseif ("-status" == mb_strtolower($current_arg,'UTF-8')) {
            $display_status = true;
        } elseif ("-token-id" == mb_strtolower($current_arg,'UTF-8')) {
            $token_id_creation = true;
        } else {
            $param_count++;
            $all_args[$param_count] = nullable_trim($current_arg);
        }
    }

    if (("" != $command) && (!$not_a_command)) {
        $command_array[] = array('command'   => $command,
                                 'param_pos' => (1 + $param_count));
    }
}


// if not enough parameters, display error message
//  and indicate how to display the help page
if (($param_count < 1) &&
    ($command != "backup-config") &&
    ($command != "call-method") &&
    ($command != "checkpam") &&
    ($command != "clearlog") &&
    ($command != "customfunction") &&
    ($command != "custominfo") &&
    ($command != "hardware") &&
    ($command != "help") &&
    ($command != "initialize-backend") &&
    ($command != "ldap-check") &&
    ($command != "ldap-users-list") &&
    ($command != "ldap-users-sync") &&
    ($command != "libhash") &&
    ($command != "lockeduserslist") &&
    ($command != "network-info") &&
    ($command != "php-version") &&
    ($command != "phpinfo") &&
    ($command != "purge-ldap-cache-folder") &&
    ($command != "purge-lock-folder") &&
    ($command != "showlog") &&
    ($command != "error-codes") &&
    ($command != "tokenslist")&&
    ($command != "update-windows-passwords") &&
    ($command != "userslist") &&
    ($command != "version") &&
    ($command != "version-only"))
{
    $command = "noparam";
    $command_array[] = array('command'   => $command,
                             'param_pos' => 1);
}


// Without any command, it should be the check command
if ('' == $command) {
    $command = "check";
    $command_array[] = array('command'   => $command,
                             'param_pos' => 1);
}


// If an environment variable is defined, we use it
$env_folder_path = getenv('MULTIOTP_PATH');
if (($env_folder_path !== false) && ($env_folder_path != '')) {
    $folder_path = $env_folder_path;
    $chdir_result = chdir($folder_path);
}


// If a base directory is given as a parameter, we use it in priority
if ('' != $base_dir) {
    $folder_path = $base_dir;
    $chdir_result = chdir($folder_path);
}


// Create a new Multiotp object
// The log and users subfolders are set by default under the folder of the script
// We set directly a specific encryption key for the config, tokens and users files
// PLEASE DO NOT CHANGE THIS LINE IF YOU DON'T KNOW WHAT YOU DO!
// IF YOU CHANGE THE ENCRYPTION KEY, YOUR PREVIOUS ENCRYPTED DATA WILL NOT BE READABLE ANYMORE

$multiotp_etc_dir = '/etc/multiotp';
$config_folder = $multiotp_etc_dir.'/config';
if (false === mb_strpos(getcwd(), '/')) {
  // if (!@file_exists($config_folder)) {
  $multiotp_etc_dir  = '';
  $config_folder = '';
}

if (($command == "libhash") || ($command == "help") || ($command == "hardware") || ($command == "version") || ($command == "php-version")) {
  if (!isset($multiotp)) {
    $multiotp = new Multiotp('DefaultCliEncryptionKey', false, $folder_path, $config_folder);
    if ($verbose_log) {
      $multiotp->EnableVerboseLog();
    }
    $multiotp->SetCredentialProviderMode($cp_mode);
    $multiotp->SetCliMode($cli_mode);
    $multiotp->SetCliProxyMode(!$cli_mode); // The CLI proxy mode is *NOT* the CLI mode
  }
} else {
  if (!isset($multiotp)) {
    $multiotp = new Multiotp('DefaultCliEncryptionKey', $initialize_backend, $folder_path, $config_folder);
    if ($verbose_log) {
      $multiotp->EnableVerboseLog();
    }
    $multiotp->SetCredentialProviderMode($cp_mode);
    $multiotp->SetCliMode($cli_mode);
    $multiotp->SetCliProxyMode(!$cli_mode); // The CLI proxy mode is *NOT* the CLI mode
    if ('' != $multiotp_etc_dir) {
      $multiotp->SetLogFolder('/var/log/multiotp/');
      $multiotp->SetConfigFolder($multiotp_etc_dir.'/config/');
      $multiotp->SetDDnsFolder($multiotp_etc_dir.'/ddns/');
      $multiotp->SetDevicesFolder($multiotp_etc_dir.'/devices/');
      $multiotp->SetGroupsFolder($multiotp_etc_dir.'/groups/');
      $multiotp->SetTokensFolder($multiotp_etc_dir.'/tokens/');
      $multiotp->SetUsersFolder($multiotp_etc_dir.'/users/');
      $multiotp->SetCredUsersFolder($multiotp_etc_dir.'/credusers/');
      $multiotp->SetCacheFolder('/tmp/cache/');
      $multiotp->SetLinuxFileMode('0666');
    }
    $multiotp->ReadConfigData();
  }
  
  $multiotp->UpgradeSchemaIfNeeded();
  $verbose_prefix = $multiotp->GetVerboseLogPrefix(); // for example Reply-Message := 
}

// Initialize multiOTP direct Credential Provider options
if ('' != $server_cache_level) {
    if ($multiotp->GetServerCacheLevel() != intval($server_cache_level)) {
        $multiotp->SetServerCacheLevel(intval($server_cache_level));
        $write_param_data = true;
        if (($multiotp->IsDeveloperMode())) {
          $multiotp->WriteLog('Developer: new server_cache_level='.$server_cache_level, false, false, 8888, 'Debug', '');
        }
    }
}
if ('' != $server_secret) {
    if ($multiotp->GetServerSecret() != $server_secret) {
        $multiotp->SetServerSecret($server_secret);
        $write_param_data = true;
        if (($multiotp->IsDeveloperMode())) {
          $multiotp->WriteLog('Developer: new server_secret='.$server_secret, false, false, 8888, 'Debug', '');
        }
    }
}
if ('' != $server_timeout) {
    if ($multiotp->GetServerTimeout() != intval($server_timeout)) {
        $multiotp->SetServerTimeout(intval($server_timeout));
        $write_param_data = true;
        if (($multiotp->IsDeveloperMode())) {
          $multiotp->WriteLog('Developer: new server_timeout='.$server_timeout, false, false, 8888, 'Debug', '');
        }
    }
}
if ('' != $server_url) {
    if ($multiotp->GetServerUrl() != $server_url) {
        $multiotp->SetServerUrl($server_url);
        $write_param_data = true;
        if (($multiotp->IsDeveloperMode())) {
          $multiotp->WriteLog('Developer: new server_url='.$server_url, false, false, 8888, 'Debug', '');
        }
    }
}
if ('' != $sync_delete_retention_days) {
    if ($multiotp->GetSyncDeleteRetentionDays() != intval($sync_delete_retention_days)) {
        $multiotp->SetSyncDeleteRetentionDays(intval($sync_delete_retention_days));
        $write_param_data = true;
        if (($multiotp->IsDeveloperMode())) {
          $multiotp->WriteLog('Developer: new sync_delete_retention_days='.$sync_delete_retention_days, false, false, 8888, 'Debug', '');
        }
    }
}
if ($write_param_data) {
    $write_result = $multiotp->WriteConfigData(array(), true);
    if (($multiotp->IsDeveloperMode())) {
        if ($write_result) {
            $multiotp->WriteLog('Developer: new configuration automatically written', false, false, 8888, 'Debug', '');
        } else {
            $multiotp->WriteLog('Developer: error during new configuration writing operation', false, false, 8888, 'Debug', '');
        }
    }
}
if (($multiotp->IsDeveloperMode())) {
    $multiotp->WriteLog('Developer: argv: '.print_r($argv, true), false, false, 8888, 'Debug', '');
}


// Initialize multiOTP options

if ($enable_log) {
  $multiotp->EnableLog();
}
if ($verbose_log) {
  $multiotp->EnableVerboseLog();
}
if ($display_log) {
  $multiotp->EnableDisplayLog();
}
if ($keep_local) {
  $multiotp->EnableKeepLocal();
}


$prefix_pin = $multiotp->IsDefaultRequestPrefixPin();
if (isset($set_prefix_pin)) {
    $prefix_pin = $set_prefix_pin;
}

$multiotp->SetSourceTag($source_tag);
$multiotp->SetSourceIp($source_ip);
$multiotp->SetSourceMac($source_mac);
$multiotp->SetCallingIp($calling_ip);
$multiotp->SetCallingMac($calling_mac);
$multiotp->SetChapId($chap_id);
$multiotp->SetChapChallenge($chap_challenge);
$multiotp->SetChapPassword($chap_password);
$multiotp->SetMsChapChallenge($ms_chap_challenge);
$multiotp->SetMsChapResponse($ms_chap_response);
$multiotp->SetMsChap2Response($ms_chap2_response);
$multiotp->SetState($state);
if ("" != $user_sid) {
  $multiotp->SetCurrentUserSid($user_sid);
}

if (($multiotp->IsDeveloperMode())) {
  $loop_start = 1;
  $temp_radius = '';
  for ($arg_loop=$loop_start; $arg_loop < $argc; $arg_loop++)
  {
    $one_radius = encode_utf8_if_needed(clean_quotes($argv[$arg_loop]));
    if (false !== mb_strpos($one_radius,' ')) {
      $one_radius = '"'.$one_radius.'"';
    }
    $temp_radius.= '{'.$one_radius.'} ';
  }
  $multiotp->WriteLog('Developer: *parameter(s) received, displayed between {}: '.nullable_trim($temp_radius), false, false, 8888, 'Debug', '');
}


// This is to be able to loop for various commands (since 5.0.3.4)
$full_args = $all_args;

for ($every_command = 0; $every_command < count($command_array); $every_command++) {
    $command   = $command_array[$every_command]['command'];
    $param_pos = $command_array[$every_command]['param_pos'];
    if (($every_command + 1) < (count($command_array))) {
        $param_count = $command_array[$every_command + 1]['param_pos'] - $param_pos;
    } else {
        $param_count = 1 + count($full_args) - $param_pos;
    }
    for ($i = 1; $i <= $param_count; $i++) {
        $all_args[$i] = $full_args[$param_pos + $i - 1];
    }
    for ($i = ($param_count + 1); $i <= $all_args_size; $i++) {
        $all_args[$i] = '';
    }

    // Be sure that non-existent parameters are empty
    for ($i = ($param_count+1); $i <= $all_args_size; $i++) {
        $all_args[$i] = '';
    }

    switch ($command) {
        case "mysql":
            if  ($param_count < 1) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } else {
                $mysql_parameters = explode(",",mb_strtolower($all_args[1],'UTF-8'));
                if (count($mysql_parameters) < 4) {
                    $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
                } else {
                    $mysql_parameters = array_pad($mysql_parameters, 7, NULL);

                    // Backend storage type
                    $multiotp->SetBackendType('mysql');

                    $multiotp->SetSqlServer($mysql_parameters[0]);
                    $multiotp->SetSqlUsername($mysql_parameters[1]);
                    $multiotp->SetSqlPassword($mysql_parameters[2]);
                    $multiotp->SetSqlDatabase($mysql_parameters[3]);
                    
                    // If table names are not defined, we keep the default value defined in the class constructor.
                    if (NULL !== $mysql_parameters[4]) {
                        $multiotp->SetSqlTableName('log', $mysql_parameters[4]);
                    }
                    if (NULL !== $mysql_parameters[5]) {
                        $multiotp->SetSqlTableName('users', $mysql_parameters[5]);
                    }
                    if (NULL !== $mysql_parameters[6]) {
                        $multiotp->SetSqlTableName('tokens', $mysql_parameters[6]);
                    }
                }
            }
            break;
        case "pgsql":
            if  ($param_count < 1) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } else {
                $pgsql_parameters = explode(",",mb_strtolower($all_args[1],'UTF-8'));
                if (count($pgsql_parameters) < 5) {
                    $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
                } else {
                    $pgsql_parameters = array_pad($pgsql_parameters, 8, NULL);

                    // Backend storage type
                    $multiotp->SetBackendType('pgsql');

                    $multiotp->SetSqlServer($pgsql_parameters[0]);
                    $multiotp->SetSqlUsername($pgsql_parameters[1]);
                    $multiotp->SetSqlPassword($pgsql_parameters[2]);
                    $multiotp->SetSqlDatabase($pgsql_parameters[3]);
                    $multiotp->SetSqlSchema($pgsql_parameters[4]);
                    
                    // If table names are not defined, we keep the default value defined in the class constructor.
                    if (NULL !== $pgsql_parameters[5]) {
                        $multiotp->SetSqlTableName('log', $pgsql_parameters[5]);
                    }
                    if (NULL !== $pgsql_parameters[6]) {
                        $multiotp->SetSqlTableName('users', $pgsql_parameters[6]);
                    }
                    if (NULL !== $pgsql_parameters[7]) {
                        $multiotp->SetSqlTableName('tokens', $pgsql_parameters[7]);
                    }
                }
            }
            break;
        case "hardware":
            $version_info = $multiotp->GetClassName()." ".$multiotp->GetVersion()." (".$multiotp->GetDate().")";
            $hardware_array = $multiotp->GetHardwareType(true);
            if ($multiotp->GetCliProxyMode()) {
                $version_info.= " [CLI PROXY]";
            } elseif ($multiotp->GetCliMode()) {
                $version_info.= " [CLI]";
            }
            if ($multiotp->GetCredentialProviderMode()) {
                $version_info.= " [CP]";
            }
            $hardware_info = "      Version: " . $version_info . $crlf;
            $hardware_info.= "  Detailed OS: " . $hardware_array['os_running'] . $crlf;
            $hardware_info.= "         Type: " . $hardware_array['type'] . $crlf;
            $hardware_info.= "       Family: " . $hardware_array['family'] . $crlf;
            $hardware_info.= "        Model: " . $hardware_array['model'] . $crlf;
            $hardware_info.= "     Hardware: " . $hardware_array['hardware'] . $crlf;
            $hardware_info.= "    CPU speed: " . $hardware_array['cpu_speed'] . $crlf;
            $hardware_info.= "       Uptime: " . $multiotp->GetUptime() . $crlf;
      
            $hardware_info.= $crlf;
            echo $hardware_info;
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "version":
            $version_info = $multiotp->GetClassName()." ".$multiotp->GetVersion()." (".$multiotp->GetDate().")";
            if ($multiotp->GetCliProxyMode()) {
                $version_info.= " [CLI PROXY]";
            } elseif ($multiotp->GetCliMode()) {
                $version_info.= " [CLI]";
            }
            if ($multiotp->GetCredentialProviderMode()) {
                $version_info.= " [CP]";
            }
            $version_info.= $crlf;
            echo $version_info;
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "version-only":
            echo $multiotp->GetVersion();
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "php-version":
            echo 'PHP '.phpversion().$crlf;
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "backup-config":
            if  (0 == $param_count) {
                // Backward compatibility
                $multiotp->BackupConfiguration();
                $result = _MULTIOTP_OPERATION_DONE_; // INFO: Requested operation successfully done
            } elseif  ($param_count < 2) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } else {
                $backup_file = ('' != nullable_trim($all_args[2])) ? $all_args[2] : 'multiotp.cfg';
                if (TRUE === ($multiotp->BackupConfiguration(array('backup_file'      => $backup_file,
                                                                   'encryption_key'   => $all_args[1])))) {
                  $result = _MULTIOTP_OPERATION_DONE_; // INFO: Requested operation successfully done
                } else {
                  $result = _MULTIOTP_ERROR_; // ERROR
                }
            }
            break;
        case "restore-config":
            if  ($param_count < 2) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } else {
                $backup_file = ('' != nullable_trim($all_args[2])) ? $all_args[2] : 'multiotp.cfg';
                if (file_exists($backup_file)) {
                    if (TRUE === ($multiotp->RestoreConfiguration(array('backup_file' => $backup_file,
                                                                        'restore_key' => $all_args[1])))) {
                      $result = _MULTIOTP_OPERATION_DONE_; // INFO: Requested operation successfully done
                    } else {
                      $result = _MULTIOTP_ERROR_; // ERROR
                    }
                } else {
                  $result = _MULTIOTP_MISSING_FILE_; // ERROR: File is missing
                }
            }
            break;
        case "call-method";
            $call_result = '';
            if (method_exists($multiotp, $call_method)) {
              if ('' != $all_args[7]) {
                $call_result = $multiotp->$call_method($all_args[1], $all_args[2], $all_args[3], $all_args[4], $all_args[5], $all_args[6], $all_args[7]);
              } elseif ('' != $all_args[6]) {
                $call_result = $multiotp->$call_method($all_args[1], $all_args[2], $all_args[3], $all_args[4], $all_args[5], $all_args[6]);
              } elseif ('' != $all_args[5]) {
                $call_result = $multiotp->$call_method($all_args[1], $all_args[2], $all_args[3], $all_args[4], $all_args[5]);
              } elseif ('' != $all_args[4]) {
                $call_result = $multiotp->$call_method($all_args[1], $all_args[2], $all_args[3], $all_args[4]);
              } elseif ('' != $all_args[3]) {
                $call_result = $multiotp->$call_method($all_args[1], $all_args[2], $all_args[3]);
              } elseif ('' != $all_args[2]) {
                $call_result = $multiotp->$call_method($all_args[1], $all_args[2]);
              } elseif ('' != $all_args[1]) {
                $call_result = $multiotp->$call_method($all_args[1]);
              } else {
                $call_result = $multiotp->$call_method();
              }
              if ($multiotp->GetVerboseFlag()) {
                $multiotp->WriteLog('Debug: *Method '.$call_method.' returned the following result: '.print_r($call_result, true), false, false, 8888, 'Debug', '');
              }
              $result = _MULTIOTP_OPERATION_DONE_;
            } else {
              if ($multiotp->GetVerboseFlag()) {
                $multiotp->WriteLog("Debug: *Method $call_method doesn't exist", false, false, 8888, 'Debug', '');
              }
              $result = _MULTIOTP_ERROR_;
            }
            if ((!is_bool($call_result)) && (!is_null($call_result)) && (!empty($call_result))) {
              if (is_array($call_result)) {
                echo print_r($call_result, true);
              } else {
                echo $call_result;
              }
            }
            break;
        case "check";
            $check_result = false;
            $self_registration = '';
            $otp_inline = '';
            if  ($param_count > 1) {
                // If the exact given user is not found, we try some different stages
                // (check also the check command)
                if (!$multiotp->CheckUserExists($all_args[1])) {
                    $check_result = false;
                    if (false !== mb_strpos($all_args[1], ':')) {
                        /*************************************************************************
                         * Here we check special cases
                         *
                         * 1) serial_number:username (for alternate self-registration process)
                         *    Do not forget to activate self-registration !
                         *
                         * 2) username:OTP (for alternate authentication with OTP and AD password)
                         *    For example in order to do MS-CHAPv2 authentication
                         *
                         *************************************************************************/
                        $part1 = mb_substr($all_args[1], 0, mb_strpos($all_args[1], ':'));
                        $part2 = mb_substr($all_args[1], mb_strpos($all_args[1], ':')+1);
                        if ($multiotp->IsSelfRegistrationEnabled() && ($multiotp->CheckTokenExists($part1))) {
                            $self_registration = $part1;
                            $all_args[1] = $part2;
                        } elseif ($multiotp->IsUserRequestLdapPasswordEnabled() && ($multiotp->CheckUserExists($part1))) {
                            $all_args[1] = $part1;
                            $otp_inline = $part2;
                        }
                    }
                    

                    /// Return a real username if the initial one is not existing
                    $find_user = $multiotp->FindRealUserName($all_args[1], TRUE);
                    if ($find_user != $all_args[1]) {
                      $all_args[1] = $find_user;
                      $multiotp->SetUser($all_args[1]);
                    }
                } else {
                    $check_result = true;
                }
                # check extension can be added here
            }
            if (($param_count < 2) && (!$encrypted_password)) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!$multiotp->ReadUserData($all_args[1])) {
                if ("ERROR" == $multiotp->GetUserEncryptionHash()) {
                    $result = _MULTIOTP_ENCRYPTION_ERROR_; // ERROR: Encryption hash error, encryption key is not the same
                } else {
                    $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
                }
            } else {
                // Resynchronization information splitting (for autoresync) is now handled in CheckToken directly
                if ('' != $all_args[3]) {
                    for ($i = 3; $i <= $all_args_size; $i++) {
                        if ('' != $all_args[$i]) {
                            $all_args[2] = $all_args[2]." ".$all_args[$i];
                        }
                    }
                }
                $result = $multiotp->CheckToken($all_args[2], '', false, false, false, false, $self_registration); // Result provided by the MultiOTP class
                if (($multiotp->IsAutoResync()) && (_MULTIOTP_TOKEN_RESYNCED_ == $result)) {
                    $result = _MULTIOTP_AUTH_OK_;
                }
            }
            break;
        case "checkpam":
            if (!$multiotp->ReadUserData(isset($_ENV["PAM_USER"])?$_ENV["PAM_USER"]:'PAM_USER_NOT_DEFINED!')) {
                if ("ERROR" == $multiotp->GetUserEncryptionHash()) {
                    $result = _MULTIOTP_ENCRYPTION_ERROR_; // ERROR: Encryption hash error, encryption key is not the same
                } else {
                    $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
                }
            } else {
                $result = $multiotp->CheckToken(isset($_ENV["PAM_AUTHTOK"])?$_ENV["PAM_AUTHTOK"]:'PAM_AUTHTOK_NOT_DEFINED!');
            }
            break;
        case "create":
        case "update":
            if (("create" == $command) && $multiotp->ReadUserData($all_args[1], true, true)) {
              $result = _MULTIOTP_USER_EXISTS_; // ERROR: user already exists.
            } elseif (("update" == $command) && (!$multiotp->ReadUserData($all_args[1], false, true))) {
              $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } elseif ($param_count < 2) {
              $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (($param_count < 3) && ("WITHOUT2FA" != mb_strtoupper($all_args[2],'UTF-8'))) {
              $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } else {
              $multiotp->SetUser($all_args[1]);
              $multiotp->SetUserPrefixPin($prefix_pin?1:0);
              
              if ($token_id_creation) {
                $key_id = $all_args[2];
                if (!$multiotp->ReadTokenData($key_id)) {
                  $result = _MULTIOTP_TOKEN_MISSING_; // ERROR: token doesn't exist.
                } else {
                  $multiotp->SetUserKeyId($key_id);
                  $multiotp->SetUserTokenSerialNumber($multiotp->GetTokenSerialNumber());
                  if (!$multiotp->SetUserAlgorithm($multiotp->GetTokenAlgorithm())) {
                    $result = _MULTIOTP_BAD_ALGORITHM_; // ERROR: invalid algorithm
                  } else {
                    $multiotp->SetUserTokenSeed($multiotp->GetTokenSeed());
                    $multiotp->SetUserTokenNumberOfDigits($multiotp->GetTokenNumberOfDigits());
                    $multiotp->SetUserTokenTimeInterval($multiotp->GetTokenTimeInterval());
                    $multiotp->SetUserTokenLastEvent($multiotp->GetTokenLastEvent());
                    $multiotp->SetUserTokenAlgoSuite($multiotp->GetTokenAlgoSuite());
                    
                    $multiotp->SetUserPin($all_args[3]);
                    
                    if ($multiotp->WriteUserData()) {
                      $result = _MULTIOTP_USER_UPDATED_; // INFO: user successfully created or updated
                    } else {
                      $result = _MULTIOTP_DATA_UPDATE_ERROR_; // ERROR: Unable to write the changes in the file
                    }
                  }
                }
              } elseif (!$multiotp->SetUserAlgorithm($all_args[2])) {
                $result = _MULTIOTP_BAD_ALGORITHM_; // ERROR: invalid algorithm
              } elseif ("WITHOUT2FA" == mb_strtoupper($all_args[2],'UTF-8')) {
                if ($multiotp->WriteUserData()) {
                  $result = _MULTIOTP_USER_UPDATED_; // INFO: user successfully created or updated
                } else {
                  $result = _MULTIOTP_DATA_UPDATE_ERROR_; // ERROR: Unable to write the changes in the file
                }
              } else {
                $seed = (("" == $all_args[3])?mb_substr(md5(date("YmdHis").mt_rand(100000,999999)),0,20).mb_substr(md5(mt_rand(100000,999999).date("YmdHis")),0,20):$all_args[3]);
                $multiotp->SetUserTokenSeed($seed);
                
                if  ($param_count < 4) {
                  $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
                } else {
                  $pin = (("" == $all_args[4]) ? $multiotp->GeneratePin() : $all_args[4]);
                  $multiotp->SetUserPin($pin);
                  if ('' == $all_args[5]) {
                      $all_args[5] = $multiotp->GetDefault2faDigits(); // Default number of digits is set to 6
                  }
                  $multiotp->SetUserTokenNumberOfDigits($all_args[5]);
                  switch (mb_strtoupper($all_args[2],'UTF-8')) {
                    // This is the time interval for mOTP
                    case "MOTP":
                      if ('' == $all_args[6]) {
                          $all_args[6] = 10; // Default windows value interval for mOTP
                      }
                      $multiotp->SetUserTokenTimeInterval($all_args[6]);
                      break;
                    // This is the time interval for TOTP
                    case "TOTP":
                      if ('' == $all_args[6]) {
                          $all_args[6] = _MULTIOTP_DEFAULT_TOTP_INTERVAL_; // Default windows value interval for TOTP
                      }
                      $multiotp->SetUserTokenTimeInterval($all_args[6]);
                      break;
                    // This is the next event for HOTP
                    case "HOTP":
                    default:
                      if ('' == $all_args[6]) {
                          $all_args[6] = 0; // Default next event
                      }
                      $multiotp->SetUserTokenLastEvent($all_args[6]-1);
                      // -1 because we are saving the last event in the user file database
                      break;
                  }
                  if ($multiotp->WriteUserData()) {
                    $result = _MULTIOTP_USER_UPDATED_; // INFO: user successfully created or updated
                  } else {
                    $result = _MULTIOTP_DATA_UPDATE_ERROR_; // ERROR: Unable to write the changes in the file
                  }
                }
              }
            }
            break;
        case "delete":
            if (!$multiotp->DeleteUser($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                $result = _MULTIOTP_USER_DELETED_; // INFO: user successfully deleted.
            }
            break;
        case "delete-token":
            if (!$multiotp->DeleteToken($all_args[1])) {
                $result = _MULTIOTP_TOKEN_MISSING_; // ERROR: token doesn't exist.
            } else {
                $result = _MULTIOTP_OPERATION_DONE_; // INFO: token successfully deleted.
            }
            break;
        case "lock":
            if (!$multiotp->LockUser($all_args[1])) { // Write is done directly
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                $result = _MULTIOTP_USER_UPDATED_; // OK, user updated
            }
            break;
        case "unlock":
            if (!$multiotp->UnlockUser($all_args[1])) { // Write is done directly
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                $result = _MULTIOTP_USER_UPDATED_; // OK, user updated
            }
            break;
        case "callapi":
            $api_result = $multiotp->CallApi(array("script_uri" => $all_args[1],
                                                   "secret"     => $all_args[2]));
            $result = (FALSE !== mb_strpos($api_result, 'result_code')) ? _MULTIOTP_OPERATION_DONE_ : _MULTIOTP_ERROR_;
            echo $api_result;
            break;
        case "assign-token":
            if (!$multiotp->AssignTokenToUser($all_args[1],
                                              $all_args[2])) {
                $result = _MULTIOTP_ERROR_; // ERROR
            } else {
                $result = _MULTIOTP_OPERATION_DONE_; // OK
            }
            break;
        case "remove-token":
            if  ($param_count < 1) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!$multiotp->ReadUserData($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                if ($multiotp->RemoveTokenFromUser($all_args[1])) {
                    $result = _MULTIOTP_OPERATION_DONE_; // OK
                } else {
                    $result = _MULTIOTP_ERROR_; // ERROR
                }
            }
            break;
        case "default-dialin-ip-mask":
            if (!$multiotp->SetDefaultDialinIpMask($all_args[1])) {
                $result = _MULTIOTP_ERROR_; // ERROR
            } elseif ($multiotp->WriteConfigData()) {
                $result = _MULTIOTP_OPERATION_DONE_; // OK
            } else {
                $result = _MULTIOTP_ERROR_; // ERROR
            }
            break;
        case "dialin-ip-address":
            if  ($param_count < 2) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!$multiotp->ReadUserData($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                $multiotp->SetUserDialinIpAddress($all_args[1], $all_args[2]);
                if ($multiotp->WriteUserData()) {
                    $result = _MULTIOTP_USER_UPDATED_; // OK, user updated
                } else {
                    $result = _MULTIOTP_ERROR_; // ERROR
                }
            }
            break;
        case "dialin-ip-mask":
            if  ($param_count < 2) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!$multiotp->ReadUserData($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                $multiotp->SetUserDialinIpMask($all_args[1], $all_args[2]);
                if ($multiotp->WriteUserData()) {
                    $result = _MULTIOTP_USER_UPDATED_; // OK, user updated
                } else {
                    $result = _MULTIOTP_ERROR_; // ERROR
                }
            }
            break;
        case "activate":
            if (!$multiotp->ReadUserData($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                $multiotp->SetUserActivated($all_args[1],1);
                if ($multiotp->WriteUserData()) {
                    $result = _MULTIOTP_USER_UPDATED_; // OK, user updated
                } else {
                    $result = _MULTIOTP_ERROR_; // ERROR
                }
            }
            break;
        case "deactivate":
        case "desactivate":
            if (!$multiotp->ReadUserData($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                $multiotp->SetUserActivated($all_args[1],0);
                if ($multiotp->WriteUserData()) {
                    $result = _MULTIOTP_USER_UPDATED_; // OK, user updated
                } else {
                    $result = _MULTIOTP_ERROR_; // ERROR
                }
            }
            break;
        case "requiresms":
            if (!$multiotp->CheckUserExists($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                $result = $multiotp->GenerateSmsToken($all_args[1]); // It writes automatically in the database
            }
            break;
        case "resync":
            if  ($param_count < 3) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!$multiotp->ReadUserData($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                if ($multiotp->ResyncToken($all_args[2], $all_args[3], $display_status)) {
                    $result = _MULTIOTP_TOKEN_RESYNCED_; // INFO: token is now synchronized
                }
            }
            break;
        case "seed":
            if  ($param_count < 3) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!$multiotp->ReadUserData($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                $result1 = $multiotp->CheckToken($all_args[2]);
                $result2 = $multiotp->CheckToken($all_args[3]);
                if ($result1 && $result2) {
                    $result = _MULTIOTP_OPERATION_DONE_;
                } else {
                    $result = _MULTIOTP_ERROR_;
                }
            }
            break;
        case "update-pin":
            if  ($param_count < 2) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!$multiotp->ReadUserData($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                $multiotp->SetUserPin($all_args[2]);
                if ($multiotp->WriteUserData()) {
                    $result = _MULTIOTP_USER_PIN_UPDATED_; // INFO: pin successfully changed
                }
            }
            break;
        case "update-windows-password":
            if  ($param_count < 1) {
              $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!$multiotp->ReadUserData($all_args[1])) {
              $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
              if ("totp" == strtolower($multiotp->GetUserAlgorithm())) {
                $otp = $multiotp->GenerateOathHotp(
                  hex2bin($multiotp->GetUserTokenSeed()),
                  intval(time() / $multiotp->GetUserTokenTimeInterval()),
                  $multiotp->GetUserTokenNumberOfDigits(),
                  $multiotp->GetUserTokenAlgoSuite()
                );
                echo "Update $all_args[1] to $otp\n";
                $result = _MULTIOTP_OPERATION_DONE_;
              }
            }
            break;
        case "update-windows-passwords":
            $deltatime = ($param_count < 1) ? 0 : abs(intval($all_args[1]));
            $user_list = nullable_trim($multiotp->GetUsersList());
            $users_array = explode("\t", $user_list);
            if (("" != $user_list) && (count($users_array) > 0)) {
              foreach ($users_array as $user) {
                $multiotp->ReadUserData($user);
                if ("totp" == strtolower($multiotp->GetUserAlgorithm())) {
                  $otp = $multiotp->GenerateOathHotp(
                    hex2bin($multiotp->GetUserTokenSeed()),
                    intval((time()-$deltatime) / $multiotp->GetUserTokenTimeInterval()),
                    $multiotp->GetUserTokenNumberOfDigits(),
                    $multiotp->GetUserTokenAlgoSuite()
                  );
                  exec("NET USER $user $otp", $output, $return);
                  if (0 !== $return) {
                    echo "Processing error for user $user\n";
                    print_r($output);
                  }
                }
              }
              $result = _MULTIOTP_OPERATION_DONE_;
            } else {
              $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            }
            break;
        case "user-info":
            $result_txt = $multiotp->GetUserInfo($all_args[1]);
            if ("" != $result_txt) {
              echo $result_txt;
              $result = _MULTIOTP_OPERATION_DONE_;
            } else {
              $result = _MULTIOTP_ERROR_;
            }
            break;
        case "set":
            $write_user_data = false;
            if  ($param_count < 2) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!$multiotp->ReadUserData($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                for ($params = 2; $params < count($all_args); $params++) {
                    $actual_array = explode("=",$all_args[$params],2);
                    if (2 == count($actual_array)) {
                        switch ($actual_array[0]) {
                            case 'cache-level':
                                $multiotp->SetUserCacheLevel(intval($actual_array[1]));
                                $write_user_data = true;
                                break;
                            case 'cache-lifetime':
                                $multiotp->SetUserCacheLifetime(intval($actual_array[1]));
                                $write_user_data = true;
                                break;
                            case 'description':
                                $multiotp->SetUserDescription($actual_array[1]);
                                $write_user_data = true;
                                break;
                            case 'email':
                                $multiotp->SetUserEmail($actual_array[1]);
                                $write_user_data = true;
                                break;
                            case 'pin':
                                $multiotp->SetUserPin($actual_array[1]);
                                $write_user_data = true;
                                break;
                            case 'ldap-pwd':
                                $multiotp->SetUserRequestLdapPassword(intval($actual_array[1]));
                                $write_user_data = true;
                                break;
                            case 'prefix-pin':
                                $multiotp->SetUserPrefixPin(intval($actual_array[1]));
                                $write_user_data = true;
                                break;
                            case 'sms':
                                $multiotp->SetUserSms($actual_array[1]);
                                $write_user_data = true;
                                break;
                            default: // Just in case we need to change additional values that have no related method
                                $internal_user_option = str_replace("-", "_", $actual_array[0]);
                                if ($multiotp->SetUserAttribute($internal_user_option, $actual_array[1]))
                                {
                                    $write_user_data = true;
                                }
                                break;
                        }
                    }
                }
                if ($write_user_data) {
                    if ($multiotp->WriteUserData()) {
                        $result = _MULTIOTP_USER_UPDATED_; // INFO: User updated
                    }
                }
            }
            break;
        case "config":
            $config_result = true;
            $write_config_data = false;
            if  ($param_count < 1)
            {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            }
            else
            {
                for ($params = 1; $params < count($all_args); $params++)
                {
                    $actual_array = explode("=",$all_args[$params],2);
                    if (2 == count($actual_array))
                    {
                        switch ($actual_array[0])
                        {
                            case 'attributes-to-encrypt':
                                $multiotp->SetAttributesToEncrypt($actual_array[1]);
                                $internal_config_option = str_replace("-", "_", $actual_array[0]);
                                if ($multiotp->SetConfigAttribute($internal_config_option, $actual_array[1]))
                                {
                                    $write_config_data = true;
                                }
                                break;
                            case 'autoresync':
                                $multiotp->SetAutoResync($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'backend-type':
                                $multiotp->SetBackendType($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'clear-otp-attribute':
                                $multiotp->SetClearOtpAttribute($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'debug':
                                $multiotp->SetDebugOption(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'display-log':
                                $multiotp->SetDisplayLogOption(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'debug-prefix':
                                $multiotp->SetVerboseLogPrefix($actual_array[1]);
                                $verbose_prefix = $multiotp->GetVerboseLogPrefix();
                                $write_config_data = true;
                                break;
                            case 'default-2fa-digits':
                                $multiotp->SetDefault2faDigits(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'default-pin-digits':
                                $multiotp->SetDefaultPinDigits(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'default-request-prefix-pin':
                                $multiotp->SetDefaultRequestPrefixPin(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'default-request-ldap-pwd':
                                $multiotp->SetDefaultRequestLdapPwd(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'group-attribute':
                                $multiotp->SetGroupAttribute($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ignore-no-prefix-cp':
                                $multiotp->SetIgnoreNoPrefixCredentialProvider(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'issuer':
                                $multiotp->SetIssuer($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-account-suffix':
                                $multiotp->SetLdapAccountSuffix($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-activated':
                                $multiotp->SetLdapActivated($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-base-dn':
                                $multiotp->SetLdapBaseDn($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-bind-dn':
                                $multiotp->SetLdapBindDn($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-cn-identifier':
                                $multiotp->SetLdapCnIdentifier($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-default-algorithm':
                                $multiotp->SetLdapDefaultAlgorithm($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-domain-controllers':
                                $multiotp->SetLdapDomainControllers($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-group-attribute':
                                $multiotp->SetLdapGroupAttribute($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-group-cn-identifier':
                                $multiotp->SetLdapGroupCnIdentifier($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-in-group':
                                $multiotp->SetLdapInGroup($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-network-timeout':
                                $multiotp->SetLdapNetworkTimeout(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'ldap-port':
                                $multiotp->SetLdapPort(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'ldap-server-password':
                                $multiotp->SetLdapServerPassword($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-server-type':
                                $multiotp->SetLdapServerType(intval($actual_array[1]), true);
                                $write_config_data = true;
                                break;
                            case 'ldap-ssl':
                                $multiotp->SetLdapSsl($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-synced-user-attribute':
                                $multiotp->SetLdapSyncedUserAttribute($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-time-limit':
                                $multiotp->SetLdapTimeLimit(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'ldap-users-dn':
                                $multiotp->SetLdapUsersDn($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'ldap-without2fa-in-group':
                                $multiotp->SetLdapWithout2faInGroup($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'log':
                                $multiotp->SetLogOption(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'log-forced-in-file':
                                $multiotp->SetLogForcedInFile(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'multiple-groups':
                                $multiotp->SetMultipleGroups(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'radius-reply-attributor':
                                $multiotp->SetRadiusReplyAttributor($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'radius-reply-separator':
                                $multiotp->SetRadiusReplySeparator($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'self-registration':
                                $multiotp->SetSelfRegistration(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'server-cache-level':
                                $multiotp->SetServerCacheLevel(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'server-cache-lifetime':
                                $multiotp->SetServerCacheLifetime(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'server-secret':
                                $multiotp->SetServerSecret($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'server-timeout':
                                $multiotp->SetServerTimeout(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'server-type':
                                $multiotp->SetServerType($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'server-url':
                                $multiotp->SetServerUrl($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-api-id':
                                $multiotp->SetSmsApiId($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-digits':
                                $multiotp->SetSmsDigits(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'sms-message':
                                $multiotp->SetSmsMessage($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-originator':
                                $multiotp->SetSmsOriginator($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-password':
                                $multiotp->SetSmsPassword($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-provider':
                                $multiotp->SetSmsProvider($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-userkey':
                            case 'sms-username':
                                $multiotp->SetSmsUsername($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-url':
                                $multiotp->SetSmsUrl($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-ip':
                                $multiotp->SetSmsIp($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-port':
                                $multiotp->SetSmsPort($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-send-template':
                                $multiotp->SetSmsSendTemplate($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-method':
                                $multiotp->SetSmsMethod($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-encoding':
                                $multiotp->SetSmsEncoding($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-status-success':
                                $multiotp->SetSmsStatusSuccess($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-content-success':
                                $multiotp->SetSmsContentSuccess($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-no-double-zero':
                                $multiotp->SetSmsNoDoubleZero($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-basic-auth':
                                $multiotp->SetSmsBasicAuth($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sms-content-encoding':
                                $multiotp->SetSmsContentEncoding($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sql-server':
                                $multiotp->SetSqlServer($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sql-username':
                                $multiotp->SetSqlUsername($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sql-password':
                                $multiotp->SetSqlPassword($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sql-database':
                                $multiotp->SetSqlDatabase($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sql-config-table':
                                $multiotp->SetSqlTableName('config',$actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sql-devices-table':
                                $multiotp->SetSqlTableName('devices',$actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sql-log-table':
                                $multiotp->SetSqlTableName('log',$actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sql-tokens-table':
                                $multiotp->SetSqlTableName('tokens',$actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sql-users-table':
                                $multiotp->SetSqlTableName('users',$actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'sync-delete-retention-days':
                                $multiotp->SetSyncDeleteRetentionDays(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'tel-default-country-code':
                                $multiotp->SetTelDefaultCountryCode($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'token-serial-number-length':
                                $multiotp->SetTokenSerialNumberLength($actual_array[1]);
                                $write_config_data = true;
                                break;
                            case 'challenge-response-enabled':
                                $multiotp->SetGlobalChallengeResponse(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'sms-challenge-enabled':
                                $multiotp->SetGlobalSmsChallenge(intval($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'text-sms-challenge':
                                $multiotp->SetGlobalTextSmsChallenge(nullable_trim($actual_array[1]));
                                $write_config_data = true;
                                break;
                            case 'text-token-challenge':
                                $multiotp->SetGlobalTextTokenChallenge(nullable_trim($actual_array[1]));
                                $write_config_data = true;
                                break;
                            default: // Just in case we need to change additional values that have no related method
                                $internal_config_option = str_replace("-", "_", $actual_array[0]);
                                if ($multiotp->SetConfigAttribute($internal_config_option, $actual_array[1]))
                                {
                                    $write_config_data = true;
                                }
                                break;
                        }
                    }
                }
                if ($write_config_data) {
                    if ($multiotp->WriteConfigData(array(), true)) {
                        $result = _MULTIOTP_OPERATION_DONE_; // INFO: Requested operation successfully done
                    }
                }
            }
            break;
        case "import":
            if (!@file_exists($all_args[1])) {
                $result = _MULTIOTP_TOKEN_MISSING_FILE_; // ERROR: Tokens definition file doesn't exist.
            } else {
                $import_password = $all_args[2];
                if (('' != $import_password) && (@file_exists($import_password))) {
                    $import_password = @file_get_contents($import_password);
                }
                if ($multiotp->ImportTokensFile($all_args[1], $all_args[1], $import_password)) {
                    $result = _MULTIOTP_TOKEN_IMPORTED_; // INFO: Tokens definition file successfully imported
                } else {
                    $result = _MULTIOTP_TOKEN_IMPORT_ERROR_; // ERROR: Tokens definition file not successfully imported.
                }
            }
            break;
        case "import-csv":
            if (!@file_exists($all_args[1])) {
                $result = _MULTIOTP_TOKEN_MISSING_FILE_; // ERROR: Tokens definition file doesn't exist.
            } else {
                if ($multiotp->ImportTokensFromCsv($all_args[1])) {
                    $result = _MULTIOTP_TOKEN_IMPORTED_; // INFO: Tokens definition file successfully imported
                } else {
                    $result = _MULTIOTP_TOKEN_IMPORT_ERROR_; // ERROR: Tokens definition file not successfully imported.
                }
            }
            break;
        case "import-pskc":
            if (!@file_exists($all_args[1])) {
                $result = _MULTIOTP_TOKEN_MISSING_FILE_; // ERROR: Tokens definition file doesn't exist.
            } else {
                $import_password = $all_args[2];
                if (('' != $import_password) && (@file_exists($import_password))) {
                    $import_password = @file_get_contents($import_password);
                }
                if ($multiotp->ImportTokensFromPskc($all_args[1], $import_password)) {
                    $result = _MULTIOTP_TOKEN_IMPORTED_; // INFO: Tokens definition file successfully imported
                } else {
                    $result = _MULTIOTP_TOKEN_IMPORT_ERROR_; // ERROR: Tokens definition file not successfully imported.
                }
            }
            break;
        case "import-yubikey":
            if (!@file_exists($all_args[1])) {
                $result = _MULTIOTP_TOKEN_MISSING_FILE_; // ERROR: Tokens definition file doesn't exist.
            } else {
                if ($multiotp->ImportYubikeyTraditional($all_args[1])) {
                    $result = _MULTIOTP_TOKEN_IMPORTED_; // INFO: Tokens definition file successfully imported
                } else {
                    $result = _MULTIOTP_TOKEN_IMPORT_ERROR_; // ERROR: Tokens definition file not successfully imported.
                }
            }
            break;
        case "import-xml":
            if (!@file_exists($all_args[1])) {
                $result = _MULTIOTP_TOKEN_MISSING_FILE_; // ERROR: Tokens definition file doesn't exist.
            } else {
                if ($multiotp->ImportTokensFromXml($all_args[1])) {
                    $result = _MULTIOTP_TOKEN_IMPORTED_; // INFO: Tokens definition file successfully imported
                } else {
                    $result = _MULTIOTP_TOKEN_IMPORT_ERROR_; // ERROR: Tokens definition file not successfully imported.
                }
            }
            break;
        case "import-alpine-xml":
            if (!@file_exists($all_args[1])) {
                $result = _MULTIOTP_TOKEN_MISSING_FILE_; // ERROR: Tokens definition file doesn't exist.
            } else {
                if ($multiotp->ImportTokensFromAlpineXml($all_args[1])) {
                    $result = _MULTIOTP_TOKEN_IMPORTED_; // INFO: Tokens definition file successfully imported
                } else {
                    $result = _MULTIOTP_TOKEN_IMPORT_ERROR_; // ERROR: Tokens definition file not successfully imported.
                }
            }
            break;
        case "import-dat":
            if (!@file_exists($all_args[1])) {
                $result = _MULTIOTP_TOKEN_MISSING_FILE_; // ERROR: Tokens definition file doesn't exist.
            } else {
                if ($multiotp->ImportTokensFromAlpineDat($all_args[1])) {
                    $result = _MULTIOTP_TOKEN_IMPORTED_; // INFO: Tokens definition file successfully imported
                } else {
                    $result = _MULTIOTP_TOKEN_IMPORT_ERROR_; // ERROR: Tokens definition file not successfully imported.
                }
            }
            break;
        case "import-sql":
            if (!@file_exists($all_args[1])) {
                $result = _MULTIOTP_TOKEN_MISSING_FILE_; // ERROR: Tokens definition file doesn't exist.
            } else {
                if ($multiotp->ImportTokensFromAuthenexSql($all_args[1])) {
                    $result = _MULTIOTP_TOKEN_IMPORTED_; // INFO: Tokens definition file successfully imported
                } else {
                    $result = _MULTIOTP_TOKEN_IMPORT_ERROR_; // ERROR: Tokens definition file not successfully imported.
                }
            }
            break;
        case "iswithout2fa":
            if  ($param_count < 1) {
              $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } else {
              // If the exact given user is not found, we try some different stages
              // (check also the iswithout2fa command)
              if (!$multiotp->CheckUserExists($all_args[1])) {
                $check_result = false;
                if (false !== mb_strpos($all_args[1], ':')) {
                  /*************************************************************************
                   * Here we check special cases
                   *
                   * 1) serial_number:username (for alternate self-registration process)
                   *    Do not forget to activate self-registration !
                   *
                   * 2) username:OTP (for alternate authentication with OTP and AD password)
                   *    For example in order to do MS-CHAPv2 authentication
                   *
                   *************************************************************************/
                  $part1 = mb_substr($all_args[1], 0, mb_strpos($all_args[1], ':'));
                  $part2 = mb_substr($all_args[1], mb_strpos($all_args[1], ':')+1);
                  if ($multiotp->IsSelfRegistrationEnabled() && ($multiotp->CheckTokenExists($part1))) {
                    $self_registration = $part1;
                    $all_args[1] = $part2;
                  } elseif ($multiotp->IsUserRequestLdapPasswordEnabled() && ($multiotp->CheckUserExists($part1))) {
                    $all_args[1] = $part1;
                    $otp_inline = $part2;
                  }
                }
                

                /// Return a real username if the initial one is not existing
                $find_user = $multiotp->FindRealUserName($all_args[1], TRUE);
                if ($find_user != $all_args[1]) {
                  $all_args[1] = $find_user;
                  $multiotp->SetUser($all_args[1]);
                }
              } else {
                $check_result = true;
              }
              # check extension can be added here
            }
            if (!$multiotp->ReadUserData($all_args[1])) {
              $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
              $result = _MULTIOTP_USER_WITH_TOKEN_; // INFO: User requires a token
              if (($multiotp->GetUserAutolockTime() > 0) && ($multiotp->GetUserAutolockTime() < time())) {
                $multiotp->DeleteUser("", TRUE);
                $multiotp->WriteLog("Error: cache too old for user ".$real_user.", cache deleted.", FALSE, FALSE, $result, 'User', $real_user);
                $result = _MULTIOTP_CACHE_TOO_OLD_ERROR_; // ERROR: Cache too old for this user
              } elseif (1 != $multiotp->GetUserActivated()) {
                if (($multiotp->GetUserAutolockTime() > 0)) {
                  $multiotp->DeleteUser("", TRUE);
                }
                $result = _MULTIOTP_USER_DISABLED_; // ERROR: User is desactivated
              } elseif ("without2fa" == mb_strtolower($multiotp->GetUserAlgorithm(),'UTF-8')) {
                $multiotp->WriteUserData(); // We cache the user locally
                $result = _MULTIOTP_USER_WITHOUT2FA_; // INFO: User can be authenticated without a token (WITHOUT2FA)
              } elseif ($multiotp->IsUserPushEnabled() && ($multiotp->GetUserPushEnableDate() > 0)) {
                $multiotp->WriteUserData(); // We cache the user locally
                $result = _MULTIOTP_USER_WITH_PUSH_; // INFO: User can use push notification
              }
            }
            break;
        case "qrcode":
            if  ($param_count < 2) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!$multiotp->CheckUserExists($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                if ($multiotp->GetUserTokenQrCode($all_args[1], '', $all_args[2])) {
                    $result = _MULTIOTP_USER_QRCODE_CREATED_; // INFO: QRcode successfully created.
                } else {
                    $result = _MULTIOTP_USER_QRCODE_CREATION_ERROR_; // INFO: QRcode not created.
                }
            }
            break;
        case "qrcodes":
            if  ($param_count < 1) {
              $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!is_dir($all_args[1])) {
              $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } else {
              $user_list = nullable_trim($multiotp->GetUsersList());
              $users_array = explode("\t", $user_list);
              if (("" != $user_list) && (count($users_array) > 0)) {
                foreach ($users_array as $user) {
                  $file = realpath($all_args[1]) . DIRECTORY_SEPARATOR . $user . '.png';
                  if ($multiotp->GetUserTokenQrCode($user, '', $file)) {
                    $result = _MULTIOTP_USER_QRCODE_CREATED_; // INFO: QRcode successfully created.
                  } else {
                    $result = _MULTIOTP_USER_QRCODE_CREATION_ERROR_; // INFO: QRcode not created.
                    break;
                  }
                }
              } else {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
              }
            }
            break;
        case "htmlinfo":
            if  ($param_count < 2) {
              if  ($param_count < 1) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
              } elseif (is_dir($all_args[1])) {
                $user_list = nullable_trim($multiotp->GetUsersList());
                $users_array = explode("\t", $user_list);
                if (("" != $user_list) && (count($users_array) > 0)) {
                  foreach ($users_array as $user) {
                    $content = $multiotp->GenerateHtmlQrCode($user);
                    if (FALSE !== file_put_contents(realpath($all_args[1]) . DIRECTORY_SEPARATOR . $user . '.html', $content)) {
                      $result = _MULTIOTP_USER_QRCODE_CREATED_; // INFO: HTML info successfully created.
                    } else {
                      $result = _MULTIOTP_USER_HTML_CREATION_ERROR_; // INFO: HTML info not created.
                      break;
                    }
                  }
                } else {
                  $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
                }
              } else {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
              }
            } elseif (!$multiotp->CheckUserExists($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                $content = $multiotp->GenerateHtmlQrCode($all_args[1]);
                if (FALSE !== file_put_contents($all_args[2], $content)) {
                  $result = _MULTIOTP_USER_QRCODE_CREATED_; // INFO: HTML info successfully created.
                } else {
                  $result = _MULTIOTP_USER_HTML_CREATION_ERROR_; // INFO: HTML info not created.
                }
            }
            break;
        case "urllink":
            if  ($param_count < 1) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } elseif (!$multiotp->CheckUserExists($all_args[1])) {
                $result = _MULTIOTP_USER_MISSING_; // ERROR: user doesn't exist.
            } else {
                if (false !== ($url_result = $multiotp->GetUserTokenUrlLink($all_args[1]))) {
                    echo $url_result.$crlf;
                    $result = _MULTIOTP_USER_URLINK_CREATED_; // INFO: UrlLink successfully created.
                } else {
                    $result = _MULTIOTP_USER_URLINK_CREATION_ERROR_; // INFO: UrlLink not created.
                }
            }
            break;
        case "scratchlist":
            echo str_replace("\t",$crlf,$multiotp->GetUserScratchPasswordsList($all_args[1])).$crlf;
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "userslist":
            echo str_replace("\t",$crlf,$multiotp->GetUsersList()).$crlf;
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "lockeduserslist":
            echo str_replace("\t",$crlf,$multiotp->GetLockedUsersList()).$crlf;
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "error-codes":
            foreach ($multiotp->GetStatusCodesArray() as $status_code => $status_text) {
              echo sprintf("%2d: $status_text", $status_code).$crlf;
            }
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "tokenslist":
            echo str_replace("\t",$crlf,$multiotp->GetTokensList()).$crlf;
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "ldap-users-list":
            if ('' != $multiotp->_config_data['ldap_domain_controllers']) {
                $ldap_users_list = $multiotp->GetLdapUsersList();
                if ('' != $ldap_users_list) {
                    echo str_replace("\t",$crlf,$ldap_users_list).$crlf;
                    $result = _MULTIOTP_OPERATION_DONE_;
                } else {
                    $result = _MULTIOTP_OPERATION_CANCELLED_;
                }
            } else {
                $result = _MULTIOTP_OPERATION_CANCELLED_;
            }
            break;
        case "ldap-user-info":
            $users_array = $multiotp->GetLdapUsersInfoArray($all_args[1], true, true);
            $user_separator = "";
            foreach ($users_array as $one_user_array) {
                echo $user_separator;
                $user_separator = $crlf;
                foreach ($one_user_array as $array_key => $array_value) {
                    if (is_array($array_value)) {
                        $info_value = "";
                        foreach ($array_value as $one_key => $one_value) {
                            $info_value.= (("" == $info_value) ? "" : ",").$one_value;
                        }
                    } else {
                        $info_value = $array_value;
                    }
                    echo mb_substr(str_repeat(" ", 23).$array_key, -23).": ".$info_value.$crlf;
                }
            }
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "ldap-users-sync":
            // All users (*), include disabled, don't ignore in groups, display debug step if next argument > 0
            if ("" == $all_args[1]) {
                $all_args[1] = 60;
            }
            $result = (($multiotp->SyncLdapUsers("*", TRUE, FALSE, intval($all_args[1]))) ? _MULTIOTP_OPERATION_DONE_ : _MULTIOTP_ERROR_);
            break;
        case "purge-ldap-cache-folder":
            $result = (($multiotp->PurgeLdapCacheFolder()) ? _MULTIOTP_OPERATION_DONE_ : _MULTIOTP_ERROR_);
            break;
        case "purge-lock-folder":
            $result = (($multiotp->PurgeLockFolder()) ? _MULTIOTP_OPERATION_DONE_ : _MULTIOTP_ERROR_);
            break;
        case "showlog":
            $multiotp->ShowLog();
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "clearlog":
            $multiotp->ClearLog();
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "log-error":
            $multiotp->WriteLog("Error: " . $all_args[1], FALSE, FALSE, _MULTIOTP_OPERATION_CANCELLED_, 'System', '', 3);
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "log-info":
            $multiotp->WriteLog("Info: " . $all_args[1], FALSE, FALSE, _MULTIOTP_OPERATION_DONE_, 'System', '', 6);
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "ldap-check":
            $result = (($multiotp->CheckLdapAuthentication()) ? _MULTIOTP_OPERATION_DONE_ : _MULTIOTP_ERROR_);
            break;
        case "check-ldap-password":
            $result = (($multiotp->CheckUserLdapPassword($all_args[1],$all_args[2])) ? _MULTIOTP_OPERATION_DONE_ : _MULTIOTP_ERROR_);
            break;
        case "fastcreate":
        case "fastcreatenopin":
        case "fastcreatewithpin":
            if ($multiotp->CheckUserExists($all_args[1])) {
                $result = _MULTIOTP_USER_EXISTS_; // ERROR: user already exists.
            } elseif  ($param_count < 1) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } else {
                if ('fastcreatenopin' == $command) {
                    $prefix_pin = false;
                } elseif ('fastcreatewithpin' == $command) {
                    $prefix_pin = true;
                }
                if ($multiotp->CreateUser($all_args[1], $prefix_pin?1:0, "TOTP", '', (''!=$all_args[2])?$all_args[2]:'')) {
                    $result = _MULTIOTP_USER_UPDATED_; // INFO: user successfully created or updated
                } else {
                    $result = _MULTIOTP_USER_CREATION_ERROR_; // ERROR: user not created
                }
            }
            break;
        case "createga":
            if ($multiotp->ReadUserData($all_args[1], true)) {
                $result = _MULTIOTP_USER_EXISTS_; // ERROR: user already exists.
            } elseif  ($param_count < 2) {
                $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
            } else {
                if ($multiotp->CreateUser($all_args[1], 0, "TOTP", bin2hex(base32_decode($all_args[2])), (''!=$all_args[3])?$all_args[3]:'')) {
                    $result = _MULTIOTP_USER_UPDATED_; // INFO: user successfully created or updated
                } else {
                    $result = _MULTIOTP_USER_CREATION_ERROR_; // ERROR: user not created
                }
            }
            break;
        case "phpinfo":
            phpinfo();
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "libhash":
            echo $multiotp->GetLibraryHash($all_args[1], $all_args[2]).$crlf;
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "customfunction":
            if (method_exists($multiotp, 'CustomFunction')) {
              echo $multiotp->CustomFunction($all_args[1], $all_args[2]).$crlf;
              $result = _MULTIOTP_OPERATION_DONE_;
            } else {
              $result = _MULTIOTP_ERROR_;
            }
            break;
        case "custominfo":
            echo $multiotp->GetCustomInfo().$crlf;
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "network-info":
            echo implode($crlf, $multiotp->GetNetworkInfo());
            echo $crlf;
            $result = _MULTIOTP_OPERATION_DONE_;
            break;
        case "noparam":
            if ($not_a_command) {
              $result = _MULTIOTP_OPERATION_DONE_;
            } else {
              $result = _MULTIOTP_PARAM_MISSING_;
              echo $multiotp->GetClassName()." ".$multiotp->GetVersion()." (".$multiotp->GetDate().")";
              if (!$no_php_info) {
                if (PHP_MAJOR_VERSION > 4) {
                  echo ", running with PHP ".phpversion();
                }
                if (is64bitPHP()) {
                  echo " [64 bits]";
                }
                if ($multiotp->GetCliProxyMode()) {
                  echo " (CLI proxy mode)";
                } else {
                  echo " (CLI mode)";
                }
              }
              echo $crlf;
              echo $multiotp->GetCopyright().$crlf;
              echo $multiotp->GetWebsite()."   (you can try the [Donate] button ;-)".$crlf;
              echo $crlf;
              echo "Not enough parameters, type multiotp -help for information about the options.";
              echo $crlf;
            }
            break;
        case "error":
            break;
        case "help":
        default:
            // Help or others, except the -initialize-backend option.
            if (!$initialize_backend) {
                $result = 999; // Info only
                echo $multiotp->GetClassName()." ".$multiotp->GetVersion()." (".$multiotp->GetDate().")";
                if (!$no_php_info) {
                    echo ", running with embedded PHP version ".phpversion();
                }
                echo $crlf;
                echo $multiotp->GetCopyright().$crlf;
                echo $multiotp->GetWebsite()."   (you can try the [Donate] button ;-)".$crlf;
                echo $crlf;
                if ($multiotp->GetVerboseFlag()) {
                    $script_folder = $multiotp->GetScriptFolder();
                    if (($detected_folder_path != '') && ($detected_folder_path != $script_folder)) {
                        echo "*Initial detected folder: ".$detected_folder_path.$crlf;
                    }
                    if ($base_dir != '') {
                        echo "*base_dir option folder: ".$folder_path.$crlf;
                    }
                    if (($env_folder_path !== false) && ($env_folder_path != '')) {
                        echo "*MULTIOTP_PATH variable folder: ".$env_folder_path.$crlf;
                    }
                    echo "*Script folder: ".$script_folder.$crlf;
                    echo $crlf;
                }
                echo "multiotp will check if the token of a user is correct, based on a specified".$crlf;
                echo "algorithm (currently Mobile-OTP (http://motp.sf.net), OATH/HOTP (RFC 4226) ".$crlf;
                echo "and OATH/TOTP (RFC 6238) are implemented). PSKC format supported (RFC 6030).".$crlf;
                echo "Supported encryption methods are PAP and CHAP.".$crlf;
                echo "Yubico OTP format supported (44 bytes long, with prefixed serial number).".$crlf;
                echo "SMS-code are supported (current providers: afilnet,aspsms,clickatell,".$crlf;
                echo "                        clickatell2,ecall,intellisms,nexmo,nowsms,smseagle,".$crlf;
                echo "                        smsgateway,swisscom,telnyx,custom,exec).".$crlf;
                echo "Specific SMS sender program supported by specifying exec as SMS provider.".$crlf;
                echo $crlf;
                echo "Google Authenticator base32_seed tokens must be of n*8 characters.".$crlf;
                echo "Google Authenticator TOTP tokens must have a 30 seconds interval.".$crlf;
                echo "Available characters in base32 are only ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".$crlf;
                echo $crlf;
                echo "To quickly create a user, use the -fastcreate option with the name of the user.".$crlf;
                echo "A quickly created user is compatible with Google Auth (30 seconds, 6 digits).".$crlf;
                echo "Depending on the prefix PIN option (WHICH IS ENABLED BY DEFAULT), a prefix PIN".$crlf;
                echo "will be requested or not before the displayed token.".$crlf;
                echo "If the PIN is not given, it is generated randomly.".$crlf;
                echo $crlf;
                echo "To quickly create a user without a prefix PIN request, use -fastcreatenopin".$crlf;
                echo $crlf;
                echo "To quickly create a user with a prefix PIN request, use -fastcreatewithpin".$crlf;
                echo $crlf;
                echo "If a token is locked (return code 24), you have to resync the token to unlock.".$crlf;
                echo "Requesting an SMS token (put sms as the password), and typing the received".$crlf;
                echo " token correctly will also unlock the token.".$crlf;
                if (!function_exists('ImageCreate')) {
                    echo $crlf;
                    echo "!!! You need to enable the gd2 library in order to create QRcode !!!".$crlf;
                }
                echo $crlf;
                echo "The check will return 0 for a correct token, and the other return code means:".$crlf;
                echo $crlf;
                echo "Return codes:".$crlf;
                echo $crlf;
                
                foreach ($multiotp->_errors_text as $key => $value) {
                    echo mb_substr("  ".$key, -2)." ".$value." ".$crlf;
                }
                echo $crlf;
                echo $crlf;
                echo "Usage:".$crlf;
                echo $crlf;
                echo " PLEASE NOTE THAT BY DEFAULT, A PREFIX PIN IS REQUIRED.".$crlf;
                echo $crlf;
                echo " multiotp user [prefix PIN]OTP (check the OTP (with prefix PIN) of the user)".$crlf;
                echo " multiotp -checkpam (to check with pam-script, using PAM_USER and PAM_AUTHTOK)".$crlf;
                echo $crlf;
                echo " multiotp -requiresms user (generate and send an SMS token to the user)".$crlf;
                echo " multiotp user sms (send an SMS token to the user)".$crlf;
                echo $crlf;
                echo " multiotp user [-chap-id=0x..] -chap-challenge=0x... -chap-password=0x...".$crlf;
                echo "   (the first byte of the chap-password value can contain the chap-id value)".$crlf;
                echo $crlf;
                echo " multiotp -fastcreate user [pin] (create a TOTP compatible token)".$crlf;
                echo " multiotp -fastcreatenopin user (create a user without a prefix PIN)".$crlf;
                echo " multiotp -fastcreatewithpin user [pin] (create a user with a prefix PIN)".$crlf;
                echo " multiotp -createga user base32_seed [pin] (create Google Auth user with TOTP)".$crlf;
                echo " multiotp -create user algo seed pin digits [pos|interval]".$crlf;
                echo " multiotp -create -token-id user token-id pin".$crlf;
                echo $crlf;
                echo "  token-id: id of the previously imported token to attribute to the user".$crlf;
                echo "      user: name of the user (should be the account name)".$crlf;
                echo "      algo: available algorithms are mOTP, HOTP, TOTP, YubicoOTP and without2FA".$crlf;
                echo "      seed: hexadecimal or base32 seed of the token".$crlf;
                echo "       pin: private pin code of the user".$crlf;
                echo "    digits: number of digits given by the token".$crlf;
                echo "       pos: for HOTP algorithm, position of the next awaited event".$crlf;
                echo "  interval: for mOTP and TOTP algorithms, token interval time in seconds".$crlf;
                echo $crlf;
                echo " multiotp -import tokens_definition_file [key|pass|key_file]".$crlf;
                echo "   (auto-detect format)".$crlf;
                echo " multiotp -import-csv csv_tokens_file.csv (tokens definition in a file)".$crlf;
                echo "   (serial_number;manufacturer;algorithm;seed;digits;interval_or_event)".$crlf;
                echo " multiotp -import-pskc pskc_tokens_file.pskc [key|pass|key_file]".$crlf;
                echo "   (PSKC format, RFC 6030)".$crlf;
                echo " multiotp -import-yubikey yubikey_traditional_format_log.csv (YubiKey)".$crlf;
                echo " multiotp -import-dat importAlpine.dat (SafeWord/Aladdin/SafeNet tokens)".$crlf;
                echo " multiotp -import-alpine-xml alpineXml.xml (SafeWord/Aladdin/SafeNet)".$crlf;
                echo " multiotp -import-xml xml_tokens_definition_file.xml (old Feitian)".$crlf;
                echo " multiotp -import-sql tokens_definition_file.sql (ZyXEL/Authenex)".$crlf;
                echo $crlf;
                echo " multiotp -iswithout2fa user (return 8 for WITHOUT2FA token, otherwise 7)".$crlf;
                echo $crlf;
                echo " multiotp -delete-token token".$crlf;
                echo $crlf;
                echo " multiotp -qrcode user png_file_name.png (only for TOTP and HOTP)".$crlf;
                echo " multiotp -urllink user (only for TOTP and HOTP, generate provisioning URL)".$crlf;
                echo " multiotp -htmlinfo user htlm_file_name.html (create file for one user) or ".$crlf;
                echo " multiotp -htmlinfo htlm_file_folder (to create all files)".$crlf;
                echo $crlf;
                echo " multiotp -scratchlist user (generate & display scratch passwords for the user)".$crlf;
                echo $crlf;
                echo " multiotp -resync [-status] user token1 token2 (two consecutive tokens)".$crlf;
                echo " multiotp -update-pin user pin".$crlf;
                echo $crlf;
                echo " multiotp -assign-token user token-id (assign the token to the user)".$crlf;
                echo " multiotp -remove-token user (remove the token assigned to the user)".$crlf;
                echo $crlf;
                echo " multiotp -default-dialin-ip-mask (set the default dialin IP mask)".$crlf;
                echo " multiotp -dialin-ip-address user ip-address (set the user dialin IP address)".$crlf;
                echo " multiotp -dialin-ip-mask user ip-address (set the user dialin IP mask)".$crlf;
                echo $crlf;
                echo " multiotp -[des]activate user".$crlf;
                echo " multiotp -[un]lock user".$crlf;
                echo $crlf;
                echo " multiotp -delete user".$crlf;
                echo $crlf;
                echo " multiotp -user-info user".$crlf;
                echo $crlf;
                echo " multiotp -config option1=value1 option2=value2 ... optionN=valueN".$crlf;
                echo "  options are  ";
                echo                "  autoresync: [0|1] enable/disable autoresync during login".$crlf;
                echo "      attributes-to-encrypt: specific attributes list to encrypt, must be".$crlf;
                echo "                             surrounded by *, like '*token_seed*user_pin*'".$crlf;
                echo "               backend-type: backend storage type (files|mysql|pgsql)".$crlf;
                echo " challenge-response-enabled: [0|1] enable/disable Challenge-Response".$crlf;
                echo "        clear-otp-attribute: attribute to return for the clear OTP".$crlf;
                echo "                             (for example 'ietf|2' for TekRADIUS)".$crlf;
                echo "                      debug: [0|1] enable/disable enhanced log information".$crlf;
                echo "                             (code result are also displayed on the console)".$crlf;
                echo "               debug-prefix: add a prefix when using the debug mode".$crlf;
                echo "                             (for example 'Reply-Message := ' for FreeRADIUS)".$crlf;
                echo "         default-2fa-digits: [6-16] set the default amount of OTP digits".$crlf;
                echo "         default-pin-digits: [4-32] set the default amount of PIN digits".$crlf;
                echo " default-request-prefix-pin: [0|1] prefix PIN enabled/disabled by default".$crlf;
                echo "   default-request-ldap-pwd: [0|1] LDAP/AD password enabled/disabled by default".$crlf;
                echo "                display-log: [0|1] enable/disable log display on the console".$crlf;
                echo "            group-attribute: attribute to return for the group membership".$crlf;
                echo "                             (for example 'Filter-Id' for FreeRADIUS)".$crlf;
                echo "        ignore-no-prefix-cp: [0|1] Disable 'no prefix' for Credential Provider".$crlf;
                echo "                     issuer: default name of the issuer of the (soft) token".$crlf;
                echo "        ldap-account-suffix: LDAP/AD account suffix".$crlf;
                echo "             ldap-activated: [0|1] enable/disable LDAP/AD support".$crlf;
                echo "               ldap-base-dn: LDAP/AD base".$crlf;
                echo "               ldap-bind-dn: LDAP/AD bind ".$crlf;
                echo "         ldap-cn-identifier: LDAP/AD cn identifier (default is sAMAccountName)".$crlf;
                echo "     ldap-default-algorithm: [totp|hotp|motp|without2fa] default algorithm".$crlf;
                echo "                             for new LDAP/AD users".$crlf;
                echo "    ldap-domain-controllers: LDAP/AD domain controller(s), comma separated".$crlf;
                echo "                ldap-filter: LDAP/AD filter customization (check documentation)".$crlf;
                echo "       ldap-group-attribute: LDAP/AD group attribute (default is memberOf)".$crlf;
                echo "   ldap-group-cn-identifier: LDAP/AD group cn identifier".$crlf;
                echo "                             (default is sAMAccountName for AD, cn for LDAP)".$crlf;
                echo "              ldap-in-group: LDAP/AD group(s) in which users should be in".$crlf;
                echo "       ldap-network-timeout: LDAP/AD network timeout (in seconds)".$crlf;
                echo "                  ldap-port: LDAP/AD port (default is set to 389)".$crlf;
                echo "       ldap-server-password: LDAP/AD server password".$crlf;
                echo "           ldap-server-type: [1|2|4] LDAP/AD server type".$crlf;
                echo "                             (1=AD, 2=standard LDAP, 4=eDirectory)".$crlf;
                echo "                   ldap-ssl: [0|1] enable/disable LDAP/AD SSL connection".$crlf;
                echo " ldap-synced-user-attribute: LDAP/AD attribute used as the account name".$crlf;
                echo "            ldap-time-limit: LDAP/AD number of sec. to wait for search results".$crlf;
                echo "              ldap-users-dn: LDAP/AD users DN (optional, use base-dn if empty)".$crlf;
                echo "                             (you can put several DN separated by semicolons)".$crlf;
                echo "   ldap-without2fa-in-group: Special LDAP/AD group(s) for without2fa users".$crlf;
                echo "            ldaptls-reqcert: ['auto'|'never'|''|...] how to perform the LDAP TLS".$crlf;
                echo "                             server certificate checks (LDAPTLS_REQCERT)".$crlf;
                echo "                             'auto' means 'never' for Windows and '' for Linux".$crlf;
                echo "       ldaptls-cipher-suite: ['auto'|''|...] which cipher suite is used for the".$crlf;
                echo "                             LDAP TLS connection (LDAPTLS_CIPHER_SUITE)".$crlf;
                echo "                             'auto' means '' for PHP higher than 5.x and".$crlf;
                echo "                             'NORMAL:!VERS-TLS1.2' for PHP 5.x and before".$crlf;
                echo "                        log: [0|1] enable/disable log permanently".$crlf;
                echo "         log-forced-in-file: [0|1] enable/disable log always in file (no DB)".$crlf;
                echo "            multiple-groups: [0|1] enable/disable multiple groups per user".$crlf;
                echo "    radius-reply-attributor: [ += |=] how to attribute a value".$crlf;
                echo "                             ('=' for TekRADIUS, ' += ' for FreeRADIUS)".$crlf;
                echo "     radius-reply-separator: [,|:|;|cr|crlf] returned attributes separator".$crlf;
                echo "                             ('crlf' for TekRADIUS, ',' for FreeRADIUS)".$crlf;
                echo "          self-registration: [1|0] enable/disable self-registration of tokens".$crlf;
                echo "         server-cache-level: [1|0] enable/disable cache from server to client".$crlf;
                echo "      server-cache-lifetime: lifetime in seconds of the cached information".$crlf;
                echo "              server-secret: shared secret used for client/server operation".$crlf;
                echo "             server-timeout: timeout value for the connection to the server".$crlf;
                echo "                server-type: [xml] type of the server".$crlf;
                echo "                             (only xml server type is able to do caching)".$crlf;
                echo "                 server-url: full url of the server(s) for client/server mode".$crlf;
                echo "                             (server_url_1;server_url_2 is accepted)".$crlf;
                echo "                 sms-api-id: SMS API id (if any, give your REST/XML API id)".$crlf;
                echo "                             with exec as provider, define the script to call".$crlf;
                echo "                               (available variables: %from, %to, %msg)".$crlf;
                echo "                 sms-digits: [6-32] set the default amount of SMS digits".$crlf;
                echo "                     sms-ip: IP address of the SMS server (for inhouse server)".$crlf;
                echo "      sms-challenge-enabled: [0|1] enable/disable SMS challenge".$crlf;
                echo "                sms-message: SMS message to display before the OTP".$crlf;
                echo "             sms-originator: SMS sender (if authorized by provider)".$crlf;
                echo "               sms-password: SMS account password".$crlf;
                echo "                   sms-port: Port of the SMS server (for inhouse server)".$crlf;
                echo "               sms-provider: SMS provider (aspsms,clickatell,clickatell2,".$crlf;
                echo "                             intellisms,nexmo,nowsms,smseagle,swisscom,telnyx,".$crlf;
                echo "                             custom,exec)".$crlf;
                echo "                sms-userkey: SMS account username or userkey".$crlf;
                echo $crlf;
                echo "Custom SMS provider only".$crlf;
                echo "                    sms-url: URL(s) of the custom SMS provider".$crlf;
                echo "                               (multiple URLs can be separated by [space],".$crlf;
                echo "                                supported variables : %api_id,%username,".$crlf;
                echo "                                %password,%from,%to,%msg,%ip,%url)".$crlf;
                echo "          sms-send-template: POST template content for custom SMS provider".$crlf;
                echo "                               (supported variables : %api_id,%username,".$crlf;
                echo "                                %password,%from,%to,%msg)".$crlf;
                echo "                 sms-method: [GET|POST|POST-JSON|POST-XML] send method".$crlf;
                echo "               sms-encoding: [ISO|UTF] characters encoding".$crlf;
                echo "         sms-status-success: status result if successful (partial supported)".$crlf;
                echo "                               (example: 20, for any 20x result)".$crlf;
                echo "        sms-content-success: content result if successful (partial supported)".$crlf;
                echo "                               (example: \"status\": \"0\")".$crlf;
                echo "       sms-content-encoding: [''|'HTML'|'URL'|'QUOTES'] Special content encoding".$crlf;
                echo "         sms-no-double-zero: [0|1] Remove double zero for international numbers".$crlf;
                echo "             sms-basic-auth: [0|1] Enable basic HTTP authentication".$crlf;
                echo "                               (sms-userkey:sms-password)".$crlf;
                echo $crlf;
                echo "                 sql-server: SQL server (FQDN or IP)".$crlf;
                echo "               sql-username: SQL username".$crlf;
                echo "               sql-password: SQL password".$crlf;
                echo "               sql-database: SQL database".$crlf;
                echo "           sql-config-table: SQL config table, default is multiotp_config".$crlf;
                echo "          sql-devices-table: SQL devices table, default is multiotp_devices".$crlf;
                echo "              sql-log-table: SQL log table, default is multiotp_log".$crlf;
                echo "           sql-tokens-table: SQL tokens table, default is multiotp_tokens".$crlf;
                echo "            sql-users-table: SQL users table, default is multiotp_users".$crlf;
                echo " sync-delete-retention-days: days of retention before deleting a no more".$crlf;
                echo "                             existing AD/LDAP user (0=disable only, no delete)".$crlf;
                echo "   tel-default-country-code: Default country code for phone number".$crlf;
                echo "         text-sms-challenge: Text displayed for the SMS challenge".$crlf;
                echo "       text-token-challenge: Text displayed for the challenge".$crlf;
                echo " token-serial-number-length: Length of the serial number of the tokens".$crlf;
                echo "                             (used for self-registration)".$crlf;
                echo $crlf;
                echo " multiotp -initialize-backend (when all options are set, it will initialize".$crlf;
                echo "                               the backend, including creating the tables)".$crlf;
                echo $crlf;
                echo " multiotp -set user option1=value1 option2=value2 ... optionN=valueN".$crlf;
                echo "  options are  email: update the email of the user".$crlf;
                echo "         cache-level: [1|0] enable/disable cache for this user on the client".$crlf;
                echo "      cache-lifetime: set/update lifetime in seconds of cached information".$crlf;
                echo "         description: set a description to the user, used for example during".$crlf;
                echo "                      the QRcode generation as the description of the account".$crlf;
                echo "               group: set/update the group of the user".$crlf;
                echo "            ldap-pwd: [0|1] the LDAP/AD password is used instead of the pin".$crlf;
                echo "                 pin: set/update the private pin code of the user".$crlf;
                echo "          prefix-pin: [0|1] the pin and the token must by merged by the user".$crlf;
                echo "                      (if your pin is 1234 and your token displays 5556677,".$crlf;
                echo "                      you will have to type 1234556677)".$crlf;
                echo "                 sms: set/update the sms phone number of the user".$crlf;
                echo $crlf;
                echo $crlf;
                echo "Authentication parameters:".$crlf;
                echo $crlf;
                echo " -calling-ip=Framed-IP-Address".$crlf;
                echo " -calling-mac=Calling-Station-Id".$crlf;
                echo " -chap-challenge=0x... CHAP-Challenge".$crlf;
                echo " -chap-id=0x... Optional CHAP-Id".$crlf;
                echo "          (the first byte of the chap-password value should contain this value)".$crlf;
                echo " -chap-password=0x... CHAP-Password".$crlf;
                echo " -mac=Called-Station-Id".$crlf;
                echo " -ms-chap-challenge=0x... MS-CHAP-Challenge".$crlf;
                echo " -ms-chap-response=0x... MS-CHAP-Response".$crlf;
                echo " -ms-chap2-response=0x... MS-CHAP2-Response".$crlf;
                echo " -src=Packet-Src-IP-Address".$crlf;
                echo " -state=State".$crlf;
                echo " -tag=Client-Shortname".$crlf;
                echo $crlf;
                echo " -usersid=Windows SID of the user (provided by multiOTP Credential Provider)".$crlf;
                echo $crlf;
                echo $crlf;
                echo "Client/server inline parameters:".$crlf;
                echo $crlf;
                echo " -server-cache-level=[1|0] enable/disable cache from server to client".$crlf;
                echo " -server-secret=shared secret used for client/server operation".$crlf;
                echo " -server-timeout=timeout value for the connection to the server".$crlf;
                echo " -server-url=full url of the server(s) for client/server mode".$crlf;
                echo "             (-server-url=server_url_1;server_url_2 is accepted)".$crlf;
                echo $crlf;
                echo $crlf;
                echo "AD/LDAP integration:".$crlf;
                echo $crlf;
                echo " multiotp -ldap-check          : check the AD/LDAP connection".$crlf;
                echo " multiotp -ldap-user-info user : print the AD/LDAP information for this user".$crlf;
                echo " multiotp -ldap-users-list     : print the list of selected the AD/LDAP users".$crlf;
                echo " multiotp -ldap-users-sync     : launch the AD/LDAP synchronization".$crlf;
                echo "                                 (will check first if a lock file is present)".$crlf;
                echo " multiotp -sync-delete-retention-days=days of retention before deleting a no".$crlf;
                echo "                                      more existing AD/LDAP user".$crlf;
                echo "                                      (0=disable only the user, do not delete)".$crlf;
                echo $crlf;
                echo $crlf;
                echo "Backup/restore commands:".$crlf;
                echo $crlf;
                echo " multiotp -backup-config  password [file-name]".$crlf;
                echo " multiotp -restore-config password [file-name]".$crlf;
                echo "   By default, the file name is multiotp.cfg in the current folder.".$crlf;
                echo $crlf;
                echo $crlf;
                echo "Other information commands:".$crlf;
                echo $crlf;
                echo " multiotp -phpinfo         : print the current PHP version".$crlf;
                echo " multiotp -showlog         : print the log entries".$crlf;
                echo " multiotp -clearlog        : clear the log entries".$crlf;
                echo " multiotp -tokenslist      : print the list of the tokens".$crlf;
                echo " multiotp -userslist       : print the list of the users".$crlf;
                echo " multiotp -lockeduserslist : print the list of the locked users".$crlf;
                echo $crlf;
                echo $crlf;
                echo "Special commands: ".$crlf;
                echo $crlf;
                echo " multiotp -purge-lock-folder".$crlf;
                echo "   This will delete the .lock files in the lock folder.".$crlf;
                echo "   .lock files are used to handle multiple instances.".$crlf;
                echo "   They are valid by default for 5 minutes.".$crlf;
                echo $crlf;
                echo " multiotp -purge-ldap-cache-folder".$crlf;
                echo "   This will delete the .cache files in the AD/LDAP cache folder.".$crlf;
                echo "   .cache files are used to speed up the AD/LDAP synchronizsation process.".$crlf;
                echo "   They are valid by default for 60 minutes.".$crlf;
                echo $crlf;
                echo " multiotp -log-error \"log information\"".$crlf;
                echo "   This will write \"log information\" error in the default log storage.".$crlf;
                echo $crlf;
                echo " multiotp -log-info \"log information\"".$crlf;
                echo "   This will write \"log information\" info in the default log storage.".$crlf;
                echo $crlf;
                echo $crlf;
                echo "Other parameters:".$crlf;
                echo $crlf;
                echo " -base-dir=/full/path/to/the/main/folder/of/multiotp/".$crlf;
                echo "           (if the script folder is wrongly detected, this will fix the issue)".$crlf;
                echo $crlf;
                echo $crlf;
                echo "Switches:".$crlf;
                echo $crlf;
                echo " -debug          Enhanced log information activated and code result on console".$crlf;
                echo "                 (the permanent state of debug can be set with -config debug=1)".$crlf;
                echo " -display-log    Log information will also be displayed on the console".$crlf;
                echo "                 (the permanent state can be set with -config display-log=1)".$crlf;
                echo " -help           Display this help page".$crlf;
                echo " -keep-local     Keep local user even if the server doesn't have it".$crlf;
                echo "                 (if the server doesn't have it, the local one will be checked)".$crlf;
                echo " -log            Log operation in the log subdirectory or in the database".$crlf;
                echo "                 (the permanent state of log can be set with -config log=1)".$crlf;
                echo " -network-info   Display network info (mode, ip, mask, gateway, dns1, dns2)".$crlf;
                echo " -nt-key-only    Return ONLY NT_KEY to the radius server".$crlf;
                echo " -param          All parameters are logged for debugging purposes".$crlf;
                echo " -php-version    Display the current version of the running PHP interpreter".$crlf;
                echo " -request-nt-key Return NT_KEY with the other attributes to the radius server".$crlf;


                echo " -error-codes    List all error codes".$crlf;
                echo " -status         Display a status bar during resynchronization".$crlf;
                echo " -version        Display the current version of the library".$crlf;
                echo $crlf;
                echo $crlf;
                echo "Examples:".$crlf;
                echo $crlf;
                echo " multiotp -fastcreate gademo".$crlf;
                echo " multiotp -debug -createga gauser 2233445566777733".$crlf;
                echo " multiotp -debug -create alan TOTP 3683453456769abc3452 2233 6 60".$crlf;
                echo " multiotp -debug -set alan prefix-pin=1".$crlf;
                echo " multiotp -debug -create anna TOTP 56821bac24fbd2343393 4455 6 ".strval(_MULTIOTP_DEFAULT_TOTP_INTERVAL_).$crlf;
                echo " multiotp -debug -set anna prefix-pin=0".$crlf;
                echo " multiotp -debug -create john HOTP 31323334353637383930 5678 6 137".$crlf;
                echo " multiotp -debug -create -token-id rick 2010090201901 2345".$crlf;
                echo " multiotp -log -create jimmy mOTP 004f5a158bca13984d349a7f23 1234 6 10".$crlf;
                echo $crlf;
                echo " multiotp -set gademo description=\"VPN code for gademo\"".$crlf;
                echo " multiotp -set jimmy sms=41791234567".$crlf;
                echo $crlf;
                echo " multiotp jimmy sms".$crlf;
                echo $crlf;
                echo " multiotp -scratchlist gademo".$crlf;
                echo $crlf;
                echo " multiotp -display-log -log -debug jimmy ea2315".$crlf;
                echo " multiotp -display-log -log anna 546078".$crlf;
                echo " multiotp -display-log -log -checkpam".$crlf;
                echo " multiotp john 5678124578".$crlf;
                echo $crlf;
                echo " multiotp -debug -import tokens.pskc \"1234 5678 9012 3456 7890 1234 5678 9012\"".$crlf;
                echo " multiotp -debug -import-pskc tokens.pskc \"qwerty\"".$crlf;
                echo " multiotp -debug -import 10OTP_data01_upgrade.sql".$crlf;
                echo " multiotp -debug -import-dat importAlpine.dat".$crlf;
                echo $crlf;
                echo " multiotp -debug -qrcode gademo gademo.png".$crlf;
                echo " multiotp -debug -urllink john".$crlf;
                echo $crlf;
                echo " multiotp -resync john 5678456789 5678345231".$crlf;
                echo " multiotp -resync -status anna 4455487352 4455983513".$crlf;
                echo " multiotp -update-pin alan 4417".$crlf;
                echo $crlf;
                echo " multiotp -config debug-prefix=\"Reply-Message := \"".$crlf;
                echo $crlf;
                echo " multiotp -config server-cache-level=1 server-cache-lifetime=15552000".$crlf;
                echo " multiotp -config server-secret=MySharedSecret server-type=xml".$crlf;
                echo " multiotp -config server-timeout=3".$crlf;
                echo " multiotp -config server-url=http://my.server/multiotp/;my.server2:8112/secure/".$crlf;
                echo $crlf;
                echo " multiotp -config sms-provider=clickatell sms-userkey=CL1 sms-password=PASS".$crlf;
                echo " multiotp -config sms-api-id=1234567".$crlf;
                echo " multiotp -config sms-message=\"Your SMS-code is:\" sms-originator=Company".$crlf;
                echo " multiotp -config sms-message=\"Type %s as code\" sms-originator=0041797654321".$crlf;
                echo $crlf;
                echo " multiotp -config sms-provider=exec sms-api-id=\"/path/to/app %from %to \"%msg\"\"".$crlf;
                echo $crlf;
                echo " multiotp -config token-serial-number-length=10,12".$crlf;
                echo $crlf;
                echo " multiotp -config backend-type=mysql sql-server=fqdn.or.ip sql-database=dbname".$crlf;
                echo " multiotp -config sql-username=user sql-password=pass".$crlf;
                echo " multiotp -initialize-backend".$crlf;
                echo $crlf;
                echo " multiotp -config backend-type=pgsql sql-server=fqdn.or.ip sql-database=dbname".$crlf;
                echo " multiotp -config sql-schema=schemaname sql-username=user sql-password=pass".$crlf;
                echo " multiotp -initialize-backend".$crlf;
                echo $crlf;
                echo $crlf;
                echo "multiOTP can be combined with a Raspberry Pi (http://www.raspberrypi.org/) in".$crlf;
                echo "order to have a very low budget strong authentication device. Please look at".$crlf;
                echo "the readme file in order to learn how to set it up in a few steps.".$crlf;
                echo "The distribution is already optimized with an HTTP proxy to speed up the CLI.".$crlf;
                echo "A ready to use binary image can be downloaded at https://download.multiOTP.net/".$crlf;
                echo $crlf;
                echo "multiOTP open source is also available as a ready to use virtual appliance in".$crlf;
                echo "standard OVA, VMware optimized or Hyper-V formats.".$crlf;
                echo "Virtual appliance images can be downloaded at https://download.multiOTP.net/".$crlf;
                echo $crlf;
                echo "multiOTP web service is working fine with any web server supporting PHP.".$crlf;
                echo " - nginx is a light one under Linux and Windows (http://nginx.org/)".$crlf;
                echo " - Mongoose is a light one under Windows (http://code.google.com/p/mongoose/)".$crlf;
                echo " - and many others like Apache HTTP Server (http://httpd.apache.org/)".$crlf;
                echo $crlf;
                echo "multiOTP is working fine with FreeRADIUS under Linux (http://freeradius.org/)".$crlf;
                echo $crlf;
                echo "multiOTP is working fine under Windows with WinRADIUS, a port of FreeRADIUS".$crlf;
                echo "(http://winradius.eu/)".$crlf;
                echo $crlf;
                echo "When used with TekRADIUS (http://www.tekradius.com) the External-Executable".$crlf;
                echo "must be called like this: C:\multiotp\multiotp.exe %ietf|1% %ietf|2%".$crlf;
                echo "Check the readme file for more information".$crlf;
                echo $crlf;
                echo $crlf;
                echo "Some of other products and services based on multiOTP:".$crlf;
                echo " multiOTP Credential Provider (https://download.multiotp.net/)".$crlf;
                echo "  Open-source Credential Provider for Windows Logon, based on MultiotpCPV2RDP".$crlf;
                echo " MultiotpCPV2RDP (https://github.com/arcadejust/MultiotpCPV2RDP)".$crlf;
                echo "  Open-source Credential Provider for Windows Logon, by arcadejust".$crlf;
                echo " mOTP-CP (https://goo.gl/Y8g4ON)".$crlf;
                echo "  Open-source Credential Provider for Windows Logon, by Last Squirrel IT".$crlf;
                echo " ownCloud OTP (https://goo.gl/mKjt43)".$crlf;
                echo "  Open-source One Time Password app for ownCloud (http://owncloud.org)".$crlf;
                echo " UserCredential (https://github.com/cymapgt/UserCredential)".$crlf;
                echo "  Open-source authentication PHP library by Cyril Ogana".$crlf;
                echo " multiOTP Pro 501V (https://www.multiotp.com)".$crlf;
                echo "  Pro version virtual appliance, with full web GUI, 1 free user licence".$crlf;
                echo " multiOTP Pro 420B (https://www.multiotp.com)".$crlf;
                echo "  Pro version tiny hardware device (BeagleBone Black), with full web GUI".$crlf;
                echo " multiOTP Enterprise (http:s//firmware.multiotp.com/enterprise/)".$crlf;
                echo "  Enterprise version virtual appliance, with HA master-slave support,".$crlf;
                echo "   also available as a Raspberry Pi image file".$crlf;
                echo " secuPASS.net (https://www.secuPASS.net)".$crlf;
                echo "  simple SMS trusting service for free WLAN Hotspot".$crlf;
                echo $crlf;
                echo "Don't hesitate to send us an email if your product uses our multiOTP library.".$crlf;
                echo $crlf;
                echo "Visit https://forum.multiotp.net/ for additional support".$crlf;
                echo $crlf;
                echo $crlf;
            }
            break;
    } // switch

    if ($param_info_debug) {
        $param_info = '';
        foreach ($all_args as $one_arg) {
            if ('' != $one_arg) {
                $param_info .= $one_arg.' ';
            }
        }
        $multiotp->WriteLog("Debug: *parameters used with command $command: ".nullable_trim($param_info), false, false, 8888, 'Debug', '');
    }

    if ($result >= 20) {
        break; // Error, we don't do the loop for the other commands
    }

} // for (new since 5.0.3.4, to be able to do multiple commands at once


if ($command != "libhash") {
    if ($initialize_backend) {
        $result = $multiotp->InitializeBackend(); // = 0xx
    }

    if (999 == $result) { // Help page only, we don't want to display the result code in this case
        $result = _MULTIOTP_PARAM_MISSING_; // ERROR: At least one parameter is missing
    } else {
        $reply_message = '';
        // Log the result
        $result_log = $result.' '.(isset($multiotp->_errors_text[$result])?$multiotp->_errors_text[$result]:'');
        if ($multiotp->GetVerboseFlag()) {
            $reply_message = $result.' *'.(isset($multiotp->_errors_text[$result])?$multiotp->_errors_text[$result]:'');
        }
        if ($verbose_prefix != '') {
            $reply_message = $result;
            if ($multiotp->GetVerboseFlag()) {
                $reply_message.=' *'.(isset($multiotp->_errors_text[$result])?$multiotp->_errors_text[$result]:'');
            }
            $reply_message = $verbose_prefix."\"".$reply_message."\"";
            $result_log = $verbose_prefix."\"".$result_log."\"";
        }
        if ($multiotp->GetVerboseFlag()) {
            $multiotp->WriteLog('Debug: *'.$result_log, false, true, 8888, 'Debug', '');
        }
        if ($multiotp->GetDisplayLogFlag()) {
            echo $reply_message.$crlf;
        }

        // echo "DEBUG: $reply_message / $result / $verbose_prefix \n";
        if ($result >= 20) {
            if ('' != $verbose_prefix) {
                $multiotp->AddReplyArrayForRadius($verbose_prefix."\"".(isset($multiotp->_errors_text[$result]) ? $multiotp->_errors_text[$result] : $result)."\"");
            } elseif ($multiotp->IsRadiusErrorReplyMessage()) {
                $multiotp->AddReplyArrayForRadius("Reply-Message := \"".(isset($multiotp->_errors_text[$result]) ? $multiotp->_errors_text[$result] : $result)."\"");
            }
        }

        $radius_additional = '';
        $radius_separator = '';

        if (count($multiotp->GetReplyArrayForRadius()) > 0) {
            $ignore_radius_array = explode(";","xxxx;yyyy");
            foreach ($multiotp->GetReplyArrayForRadius() as $one_radius_message) {
                $ignore_attribute = false;
                $current_attribute = nullable_trim(mb_substr($one_radius_message, 0, mb_strpos($one_radius_message, nullable_trim($multiotp->GetRadiusReplyAttributor()))));
                foreach ($ignore_radius_array as $one_ignore_attribute) {
                    if (false !== mb_strpos(mb_strtoupper($current_attribute,'UTF-8'),mb_strtoupper($one_ignore_attribute,'UTF-8'))) {
                        $ignore_attribute = true;
                    }
                }
                if (!$ignore_attribute) {
                    $radius_additional.= $radius_separator.$one_radius_message;
                    $radius_separator = $multiotp->GetRadiusReplySeparator();
                }
            }
        }
        if ($request_nt_key || $nt_key_only) {
            $nt_key = nullable_trim($multiotp->GetNtKey());
            if ('' != $nt_key) {
              if ($nt_key_only) {
                $radius_additional = "NT_KEY: ".$nt_key.$crlf;
              } else {
                $radius_additional.= $radius_separator."NT_KEY: ".$nt_key.$crlf;
              }
            }
        }
        if (0 < mb_strlen($radius_additional)) {
            if ($multiotp->GetVerboseFlag()) {
                $multiotp->WriteLog('Debug: *Attributes sent to the RADIUS server: '.$radius_additional, false, false, 8888, 'Debug', '');
            }
          echo $radius_additional."\r\n";
        }
    }
}

if (!$cli_mode) {
    header('X-multiOTP-Error-Level: '.intval($result));
    ob_end_flush();
}

if ($multiotp->GetCredentialProviderMode()) {
    echo "multiOTP Credential Provider mode";
}

if (!defined('CLI_NO_EXIT_CODE')) {
  exit(intval($result));
}
?>