Browse Source

Merge branch 'develop' into feature/mess_around_with_transport_layer

Strobel, Stefan | Friedrich Lütze GmbH 1 year ago
parent
commit
d84635fbbe

+ 21 - 0
.vscode/launch.json

@@ -66,6 +66,27 @@
             ],
             ],
             "preLaunchTask": "make integrationtest",
             "preLaunchTask": "make integrationtest",
             "miDebuggerPath": "/usr/bin/gdb"
             "miDebuggerPath": "/usr/bin/gdb"
+        },
+        {
+            "name": "debug integrationtest_auth",
+            "type": "cppdbg",
+            "request": "launch",
+            "program": "${workspaceFolder}/output/test/integrationtest_auth/integrationtest_auth",
+            "args": [],
+            "stopAtEntry": false,
+            "cwd": "${workspaceFolder}",
+            "environment": [],
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "setupCommands": [
+                {
+                    "description": "Enable pretty-printing for gdb",
+                    "text": "-enable-pretty-printing",
+                    "ignoreFailures": true
+                }
+            ],
+            "preLaunchTask": "make integrationtest_auth",
+            "miDebuggerPath": "/usr/bin/gdb"
         }
         }
     ]
     ]
 }
 }

+ 0 - 13
.vscode/settings.json

@@ -1,13 +0,0 @@
-{
-    "files.associations": {
-        "random": "cpp",
-        "*.tcc": "cpp",
-        "string": "cpp",
-        "vector": "cpp",
-        "fstream": "cpp",
-        "limits": "cpp",
-        "sstream": "cpp",
-        "utility": "cpp",
-        "algorithm": "cpp"
-    }
-}

+ 8 - 0
.vscode/tasks.json

@@ -38,6 +38,14 @@
                 "$gcc"
                 "$gcc"
             ]
             ]
         },
         },
+        {
+            "label": "make integrationtest_auth",
+            "type": "shell",
+            "command": "make integrationtest_auth",
+            "problemMatcher": [
+                "$gcc"
+            ]
+        },
         {
         {
             "label": "make test",
             "label": "make test",
             "type": "shell",
             "type": "shell",

+ 9 - 7
README.md

@@ -35,6 +35,7 @@ The `shellmatta` piled up some features over time:
 2. auto complete
 2. auto complete
 3. heredoc like interface to pass multiline data
 3. heredoc like interface to pass multiline data
 4. option parser (getopt like)
 4. option parser (getopt like)
+5. simple authentication mechanism
 
 
 ## Documentation
 ## Documentation
 
 
@@ -140,13 +141,14 @@ int main(void)
 
 
 There are some defines you can use to change the behaviour of the shellmatta:
 There are some defines you can use to change the behaviour of the shellmatta:
 
 
-| Define                     | Description                                    |
-| -------------------------- | ---------------------------------------------- |
-| SHELLMATTA_STRIP_PRINTF    | removes stdio dependencies to reduce footprint |
-| SHELLMATTA_HELP_COMMAND    | string to overwrite the help command name      |
-| SHELLMATTA_HELP_ALIAS      | string to overwrite the help command alias     |
-| SHELLMATTA_HELP_HELP_TEXT  | string to overwrite the help command help      |
-| SHELLMATTA_HELP_USAGE_TEXT | string to overwrite the help command usage     |
+| Define                                 | Description                                    |
+| -------------------------------------- | ---------------------------------------------- |
+| SHELLMATTA_STRIP_PRINTF                | removes stdio dependencies to reduce footprint |
+| SHELLMATTA_HELP_COMMAND                | string to overwrite the help command name      |
+| SHELLMATTA_HELP_ALIAS                  | string to overwrite the help command alias     |
+| SHELLMATTA_HELP_HELP_TEXT              | string to overwrite the help command help      |
+| SHELLMATTA_HELP_USAGE_TEXT             | string to overwrite the help command usage     |
+| SHELLMATTA_AUTHENTICATION              | if defined this enables the authentication     |
 
 
 ## Example
 ## Example
 
 

+ 121 - 5
api/shellmatta.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -25,6 +25,21 @@
 
 
 /* global defines */
 /* global defines */
 
 
+
+/*
+ * Define the printf format specifier for all GCC versions > 3.3
+ * This will let the compiler know that shelmatta_printf() is a function taking printf-like format specifiers.
+ */
+#ifndef SHELLMATTA_ATTR_FORMAT
+#   if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)))
+#       define SHELLMATTA_ATTR_FORMAT(fmt, args) __attribute__((__format__(__printf__, fmt, args)))
+#   else
+#       define SHELLMATTA_ATTR_FORMAT(fmt, args)
+#   endif
+#else
+#   define SHELLMATTA_ATTR_FORMAT(fmt, args)
+#endif
+
 /**
 /**
  * @brief definition of a shellmatta handle
  * @brief definition of a shellmatta handle
  */
  */
@@ -101,6 +116,66 @@ typedef shellmatta_retCode_t (*shellmatta_cmdFct_t)(const shellmatta_handle_t
  */
  */
 typedef shellmatta_retCode_t (*shellmatta_write_t)(const char* data, uint32_t length);
 typedef shellmatta_retCode_t (*shellmatta_write_t)(const char* data, uint32_t length);
 
 
+#ifdef SHELLMATTA_AUTHENTICATION
+/**
+ * @brief user role matrix
+ */
+typedef struct
+{
+    uint32_t    userId;     /**< id of the user (!= 0)                              */
+    bool        superuser;  /**< allow the user to access all commands              */
+    const char  *username;  /**< name of the user role                              */
+    const char  *password;  /**< password of the user role or NULL (custom auth)    */
+} shellmatta_auth_user_t;
+
+/**
+ * @brief typedefinition of one line of the authentication table
+ */
+typedef struct
+{
+    const char*    cmd;           /**< command to grand access to               */
+    const uint32_t *userIds;      /**< list of user ids with access to the cmd  */
+    const uint32_t userIdslength; /**< length of the user list                  */
+} shellmatta_auth_perm_t;
+
+/**
+ * @brief login states
+ */
+typedef enum
+{
+    SHELLMATTA_AUTH_IDLE,       /**< authentication system waits    */
+    SHELLMATTA_AUTH_USERNAME,   /**< input of username              */
+    SHELLMATTA_AUTH_PASSWORD    /**< input of password              */
+} shellmatta_auth_state_t;
+
+/**
+ * @brief log actions - passed to the log function
+ */
+typedef enum
+{
+    SHELLMATTA_AUTH_EVENT_NONE,         /**< no event (init value)      */
+    SHELLMATTA_AUTH_EVENT_LOGIN,        /**< successful login event     */
+    SHELLMATTA_AUTH_EVENT_LOGIN_FAILED, /**< failed login event         */
+    SHELLMATTA_AUTH_EVENT_LOGOUT,       /**< succesful logout event     */
+} shellmatta_auth_log_event_t;
+
+/**
+ * @brief custom shellmatta authentication method
+ * @param[in]   userId      user id to log in (name of the user role)
+ * @param[in]   password    password for the login
+ * @return      userId if password was correct - otherwise 0
+ */
+typedef shellmatta_retCode_t (*shellmatta_auth_check_t)(const uint32_t userId, const char* password);
+
+/**
+ * @brief shellmatta authentication log method - will be called whenever a login attempt is done
+ * @param[in]   userId  userId to be logged in (0 on unknown user)
+ * @param[in]   event   event type to be logged (e.g. successful login)
+ */
+typedef void (*shellmatta_auth_log_t)(const uint32_t userId, shellmatta_auth_log_event_t event);
+
+#endif
+
 /**
 /**
  * @brief structure of one shellmatta command
  * @brief structure of one shellmatta command
  */
  */
@@ -110,7 +185,10 @@ typedef struct shellmatta_cmd
     char                    *cmdAlias;  /**< command alias                          */
     char                    *cmdAlias;  /**< command alias                          */
     char                    *helpText;  /**< help text to print in "help" command   */
     char                    *helpText;  /**< help text to print in "help" command   */
     char                    *usageText; /**< usage text - printed on "help cmd"     */
     char                    *usageText; /**< usage text - printed on "help cmd"     */
-    shellmatta_cmdFct_t     cmdFct;     /**< pointer to the cmd callback function   */
+    shellmatta_cmdFct_t     cmdFct;     /**< pointer to the cmd callack function    */
+#ifdef SHELLMATTA_AUTHENTICATION
+    shellmatta_auth_perm_t  *authLink;  /**< internally used - pointer to perm list */
+#endif
     struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
     struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
 } shellmatta_cmd_t;
 } shellmatta_cmd_t;
 
 
@@ -224,6 +302,20 @@ typedef struct
     bool                            cmdListIsConst;     /**< true if the #cmdList was passed during
     bool                            cmdListIsConst;     /**< true if the #cmdList was passed during
                                                              initialization                         */
                                                              initialization                         */
     shellmatta_opt_t                optionParser;       /**< option parser sructure                 */
     shellmatta_opt_t                optionParser;       /**< option parser sructure                 */
+#ifdef SHELLMATTA_AUTHENTICATION
+    shellmatta_auth_state_t         loginState;         /**< state variable of the login cmd        */
+    shellmatta_cmd_t                loginCmd;           /**< login command structure                */
+    shellmatta_cmd_t                logoutCmd;          /**< logout command structure               */
+    uint32_t                        userId;             /**< user ID of the current session         */
+    uint32_t                        tmpUserId;          /**< remporary user ID during input         */
+    shellmatta_auth_user_t          *userPointer;       /**< pointer to the user in the user list   */
+    shellmatta_auth_user_t          *userList;          /**< user list                              */
+    uint32_t                        userListLength;     /**< length of the user list                */
+    shellmatta_auth_perm_t          *permList;          /**< permission list                        */
+    uint32_t                        permListLength;     /**< length of the permission list          */
+    shellmatta_auth_check_t         checkFct;           /**< custom credential check function       */
+    shellmatta_auth_log_t           logFct;             /**< auth log function                      */
+#endif
 #ifdef SHELLMATTA_TRANSPORT_ENABLE
 #ifdef SHELLMATTA_TRANSPORT_ENABLE
     uint32_t                        transportBusyMark;  /**< transport processing position during 
     uint32_t                        transportBusyMark;  /**< transport processing position during 
                                                              busy cmd execution                     */
                                                              busy cmd execution                     */
@@ -258,8 +350,8 @@ shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle,
 shellmatta_retCode_t shellmatta_addCmd(     shellmatta_handle_t handle,
 shellmatta_retCode_t shellmatta_addCmd(     shellmatta_handle_t handle,
                                             shellmatta_cmd_t    *cmd);
                                             shellmatta_cmd_t    *cmd);
 
 
-shellmatta_retCode_t shellmatta_removeCmd(  shellmatta_handle_t handle,
-                                            shellmatta_cmd_t    *cmd);
+shellmatta_retCode_t shellmatta_removeCmd(  shellmatta_handle_t     handle,
+                                            const shellmatta_cmd_t  *cmd);
 
 
 shellmatta_retCode_t shellmatta_configure(  shellmatta_handle_t handle,
 shellmatta_retCode_t shellmatta_configure(  shellmatta_handle_t handle,
                                             shellmatta_mode_t   mode,
                                             shellmatta_mode_t   mode,
@@ -306,7 +398,31 @@ shellmatta_retCode_t shellmatta_transport_flush(shellmatta_handle_t     handle);
 #ifndef SHELLMATTA_STRIP_PRINTF
 #ifndef SHELLMATTA_STRIP_PRINTF
 shellmatta_retCode_t shellmatta_printf(     shellmatta_handle_t handle,
 shellmatta_retCode_t shellmatta_printf(     shellmatta_handle_t handle,
                                             const char          *fmt,
                                             const char          *fmt,
-                                            ...);
+                                            ...)
+                                            SHELLMATTA_ATTR_FORMAT(2, 3);
+#endif
+
+#ifdef SHELLMATTA_AUTHENTICATION
+
+shellmatta_retCode_t shellmatta_auth_init(                  shellmatta_handle_t     handle,
+                                                            shellmatta_auth_user_t  *userList,
+                                                            uint32_t                userListLength,
+                                                            shellmatta_auth_perm_t  *permList,
+                                                            uint32_t                permListLength,
+                                                            bool                    customLogin,
+                                                            shellmatta_auth_check_t checkFct,
+                                                            shellmatta_auth_log_t   logFct);
+shellmatta_retCode_t shellmatta_auth_login(                 shellmatta_handle_t     handle,
+                                                            uint32_t                userId);
+shellmatta_retCode_t shellmatta_auth_logout(                shellmatta_handle_t     handle);
+uint32_t             shellmatta_auth_getLoggedInUserId(     shellmatta_handle_t     handle);
+shellmatta_retCode_t shellmatta_auth_getLoggedInUserName(   shellmatta_handle_t     handle,
+                                                            char                    *data,
+                                                            uint32_t                *length);
+shellmatta_retCode_t shellmatta_auth_chpasswd(              shellmatta_handle_t     handle,
+                                                            const char              *username,
+                                                            const char              *password);
+
 #endif
 #endif
 
 
 #endif
 #endif

+ 72 - 0
cfg/cppcheck/cppcheck_suppressions.xml

@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<suppressions>
+    <suppress>
+        <id>missingIncludeSystem</id>
+    </suppress>
+    <suppress>
+        <id>variableScope</id>
+        <!--Intentionally
+        not limiting the variable scope as suggested - declaring variables at
+            the top of each function.-->
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta.c</fileName>
+        <symbolName>shellmatta_doInit</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta.c</fileName>
+        <symbolName>shellmatta_resetShell</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta.c</fileName>
+        <symbolName>shellmatta_addCmd</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta.c</fileName>
+        <symbolName>shellmatta_removeCmd</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta.c</fileName>
+        <symbolName>shellmatta_configure</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta.c</fileName>
+        <symbolName>shellmatta_processData</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta.c</fileName>
+        <symbolName>shellmatta_printf</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta_opt.c</fileName>
+        <symbolName>shellmatta_opt</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta_auth.c</fileName>
+        <symbolName>shellmatta_auth_init</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta_auth.c</fileName>
+        <symbolName>shellmatta_auth_getLoggedInUserId</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta_auth.c</fileName>
+        <symbolName>shellmatta_auth_getLoggedInUserName</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta_auth.c</fileName>
+        <symbolName>shellmatta_auth_chpasswd</symbolName>
+    </suppress>
+</suppressions>

+ 9 - 0
doc/shellmatta.dox

@@ -24,6 +24,10 @@
             App -> Shellmatta: shellmatta_addCmd(command)
             App -> Shellmatta: shellmatta_addCmd(command)
         end
         end
 
 
+        alt Authentication enabled
+            App -> Shellmatta: shellmatta_auth_init()
+        end
+
         loop until finished
         loop until finished
             IO -> Shellmatta: shellmatta_processData(data)
             IO -> Shellmatta: shellmatta_processData(data)
             Shellmatta -> App: call command function
             Shellmatta -> App: call command function
@@ -33,6 +37,11 @@
     @enduml
     @enduml
 
 
 
 
+    @section shellmatta_options Shellmatta options
+
+    The Shellmatta comes with some features which can be opted in:
+
+    @subpage shellmatta_auth
     @subpage shellmatta_transport_layer
     @subpage shellmatta_transport_layer
 
 
 */
 */

+ 84 - 0
doc/shellmatta_auth.dox

@@ -0,0 +1,84 @@
+/**
+
+    @page shellmatta_auth Shellmatta Authentication
+
+    The shellmatta comes with a simple authentication mechanism.
+    It can be used to hide certain (or all) commands from users without
+    permission.
+
+    The permissions can be set per command.
+
+    To enable the shellmatta auth module you have to include the file
+    shellmatta_auth.c into your build and set the define
+    ``SHELLMATTA_AUTHENTICATION``.
+
+    Unfortunately the structure of each command has to be altered to include
+    the additional information required by the auth module.
+    Please add another NULL to the initializers of every command of type
+    #shellmatta_cmd_t.
+
+
+        shellmatta_cmd_t exampleCmd = { "example",
+                                        "e",
+                                        "example command",
+                                        "example [options]\n"
+                                        "\t-v, --version - print the version of the command",
+                                        exampleCmdFct,
+                                        NULL,
+                                        NULL};
+
+    After initializing the shellmatta instance you have to setup users with
+    username and password.
+    By enabling the user to be superuser you grant this user access to all
+    commands without the need of setting a permission list.
+
+        shellmatta_auth_user_t userList[] = {
+            {1, true, "root", "rootpw"},
+            {2, false, "shimatta", "12345678"},
+            {3, false, "not_shimatta", "87654321"}
+        };
+
+    Every command can get a permission matrix - the perm lists can be reused for
+    multiple users with the same permissions.
+    When no entry is found for a command in the permission list the command
+    defaults to be public.
+
+    It is also possible to use the userID 0 to hide a command when logged in.
+
+        uint32_t exampleCmdPerms[] = {2};
+        shellmatta_auth_perm_t permList[] = {
+            {"exampleCmd", exampleCmdPerms, sizeof(exampleCmdPerms)/sizeof(exampleCmdPerms[0])}
+        };
+
+    Now call the #shellmatta_auth_init method and pass the user and permissions
+    lists.
+    It is possible to register optional callbacks for a custom password check
+    and a log function which is called on every authentication event.
+
+        shellmatta_auth_init(handle, userList, 3, permList, 1, false, NULL, NULL);
+
+
+    @section shellmatta_auth_custom_login Custom login
+
+    By default the shellmatta uses plain text passwords.
+    This of course is not state of the art and usually highly insecure.
+
+    As most of the fancy password hashing methods are platform dependant none of
+    those is included to keep up the compatibility with as many platforms as
+    possible (sacrificing security).
+
+    To overcome this limitation you can register your own function to check the
+    credentials.
+
+    Just implement a function of type #shellmatta_auth_check_t and pass it to
+    the #shellmatta_auth_init method during initialization.
+
+        shellmatta_retCode_t custom_auth_check(const uint32_t userId, const char* password) {
+            /‌/ Check if the passed userID matches the passed password.
+            if (password_matches()) {
+                return SHELLMATTA_OK;
+            }
+            return SHELLMATTA_ERROR;
+        }
+
+*/

+ 24 - 9
example/main.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -31,7 +31,7 @@ static shellmatta_retCode_t doSomething(shellmatta_handle_t handle, const char *
     shellmatta_printf(handle, "%s - length: %u", arguments, length);
     shellmatta_printf(handle, "%s - length: %u", arguments, length);
     return SHELLMATTA_OK;
     return SHELLMATTA_OK;
 }
 }
-shellmatta_cmd_t doSomethingCmd = {"doSomething", "do", "Function does something", "use me, please", doSomething, NULL};
+shellmatta_cmd_t doSomethingCmd = {"doSomething", "do", "Function does something", "use me, please", doSomething, NULL, NULL};
 
 
 static shellmatta_retCode_t doSome(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 static shellmatta_retCode_t doSome(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 {
 {
@@ -45,7 +45,7 @@ static shellmatta_retCode_t doSome(shellmatta_handle_t handle, const char *argum
 
 
     return SHELLMATTA_OK;
     return SHELLMATTA_OK;
 }
 }
-shellmatta_cmd_t doSomeCmd = {"adoSome2", "adof2", "Function does something", "use me, please", doSome, NULL};
+shellmatta_cmd_t doSomeCmd = {"adoSome2", "adof2", "Function does something", "use me, please", doSome, NULL, NULL};
 
 
 static shellmatta_retCode_t removeCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 static shellmatta_retCode_t removeCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 {
 {
@@ -58,7 +58,7 @@ static shellmatta_retCode_t removeCmdFct(shellmatta_handle_t handle, const char
 
 
     return SHELLMATTA_OK;
     return SHELLMATTA_OK;
 }
 }
-shellmatta_cmd_t removeCommand = {"remove", "r", "Function removes a command", "", removeCmdFct, NULL};
+shellmatta_cmd_t removeCommand = {"remove", "r", "Function removes a command", "", removeCmdFct, NULL, NULL};
 
 
 
 
 static shellmatta_retCode_t quit(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 static shellmatta_retCode_t quit(shellmatta_handle_t handle, const char *arguments, uint32_t length)
@@ -71,7 +71,7 @@ static shellmatta_retCode_t quit(shellmatta_handle_t handle, const char *argumen
 
 
     return SHELLMATTA_OK;
     return SHELLMATTA_OK;
 }
 }
-shellmatta_cmd_t quitCommand = {"quit", "q", "Function quits the shell", "", quit, NULL};
+shellmatta_cmd_t quitCommand = {"quit", "q", "Function quits the shell", "", quit, NULL, NULL};
 
 
 static shellmatta_retCode_t empty(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 static shellmatta_retCode_t empty(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 {
 {
@@ -83,7 +83,7 @@ static shellmatta_retCode_t empty(shellmatta_handle_t handle, const char *argume
 
 
     return SHELLMATTA_OK;
     return SHELLMATTA_OK;
 }
 }
-shellmatta_cmd_t emptyCommand = {"empty", NULL, NULL, NULL, empty, NULL};
+shellmatta_cmd_t emptyCommand = {"empty", NULL, NULL, NULL, empty, NULL, NULL};
 
 
 static shellmatta_retCode_t reset(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 static shellmatta_retCode_t reset(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 {
 {
@@ -127,7 +127,7 @@ static shellmatta_retCode_t reset(shellmatta_handle_t handle, const char *argume
 
 
     return SHELLMATTA_OK;
     return SHELLMATTA_OK;
 }
 }
-shellmatta_cmd_t resetCommand = {"reset", NULL, "resets the shellmatta instance", "reset [--prompt true/false]", reset, NULL};
+shellmatta_cmd_t resetCommand = {"reset", NULL, "resets the shellmatta instance", "reset [--prompt true/false]", reset, NULL, NULL};
 
 
 static shellmatta_retCode_t continuous(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 static shellmatta_retCode_t continuous(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 {
 {
@@ -151,7 +151,7 @@ static shellmatta_retCode_t continuous(shellmatta_handle_t handle, const char *a
     }
     }
     return ret;
     return ret;
 }
 }
-shellmatta_cmd_t continuousCommand = {"continuous", "cont", "prints continously all input bytes", "continuous", continuous, NULL};
+shellmatta_cmd_t continuousCommand = {"continuous", "cont", "prints continously all input bytes", "continuous", continuous, NULL, NULL};
 
 
 static shellmatta_retCode_t busy(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 static shellmatta_retCode_t busy(shellmatta_handle_t handle, const char *arguments, uint32_t length)
 {
 {
@@ -173,7 +173,7 @@ static shellmatta_retCode_t busy(shellmatta_handle_t handle, const char *argumen
 
 
     return ret;
     return ret;
 }
 }
-shellmatta_cmd_t busyCommand = {"busy", NULL, NULL, NULL, busy, NULL};
+shellmatta_cmd_t busyCommand = {"busy", NULL, NULL, NULL, busy, NULL, NULL};
 
 
 
 
 shellmatta_retCode_t writeFct(const char* data, uint32_t length)
 shellmatta_retCode_t writeFct(const char* data, uint32_t length)
@@ -221,6 +221,21 @@ int main(int argc, char **argv)
     shellmatta_addCmd(handle, &continuousCommand);
     shellmatta_addCmd(handle, &continuousCommand);
     shellmatta_addCmd(handle, &busyCommand);
     shellmatta_addCmd(handle, &busyCommand);
 
 
+
+    shellmatta_auth_user_t userList[] = {
+        {1, false, "shimatta", "12345678"},
+        {2, false, "not_shimatta", "87654321"}
+    };
+
+    uint32_t doSomeCmdPerms[] = {1, 2};
+    uint32_t removeCommandPerms[] = {1};
+    shellmatta_auth_perm_t permList[] = {
+        {"adoSome2", doSomeCmdPerms, sizeof(doSomeCmdPerms)/sizeof(doSomeCmdPerms[0])},
+        {"remove", removeCommandPerms, sizeof(removeCommandPerms)/sizeof(removeCommandPerms[0])}
+    };
+
+    shellmatta_auth_init(handle, userList, 2, permList, 2, false, NULL, NULL);
+
     while(exitRequest == false)
     while(exitRequest == false)
     {
     {
         char c;
         char c;

+ 119 - 65
makefile

@@ -1,17 +1,26 @@
-# 
-# Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
-# 
+#
+# Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+#
 # This Source Code Form is subject to the terms of the Mozilla Public
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at https://mozilla.org/MPL/2.0/.
 # file, You can obtain one at https://mozilla.org/MPL/2.0/.
-# 
+#
 
 
-OBJ_DIR := output/
-INTEGRATIONTEST_CPP_OBJ_DIR := $(OBJ_DIR)test/integrationtest/
-INTEGRATIONTEST_C_OBJ_DIR := $(INTEGRATIONTEST_CPP_OBJ_DIR)
-INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT := $(OBJ_DIR)test/integrationtest_transport/
-INTEGRATIONTEST_C_OBJ_DIR_TRANSPORT := $(INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT)
-UNITTEST_OBJ_DIR := $(OBJ_DIR)test/unittest/
+OBJ_DIR                                 := output/
+OBJ_DIR_EXAMPLE                         := $(OBJ_DIR)example_obj/
+INTEGRATIONTEST_CPP_OBJ_DIR             := $(OBJ_DIR)test/integrationtest/
+INTEGRATIONTEST_AUTH_CPP_OBJ_DIR        := $(OBJ_DIR)test/integrationtest_auth/
+INTEGRATIONTEST_C_OBJ_DIR               := $(INTEGRATIONTEST_CPP_OBJ_DIR)
+INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT   := $(OBJ_DIR)test/integrationtest_transport/
+INTEGRATIONTEST_C_OBJ_DIR_TRANSPORT     := $(INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT)
+INTEGRATIONTEST_AUTH_C_OBJ_DIR          := $(INTEGRATIONTEST_AUTH_CPP_OBJ_DIR)
+UNITTEST_OBJ_DIR                        := $(OBJ_DIR)test/unittest/
+
+# helper define to run tests from a list
+define \n
+
+
+endef
 
 
 CC  := gcc
 CC  := gcc
 CPP := g++
 CPP := g++
@@ -28,6 +37,23 @@ SOURCES :=  src/shellmatta.c                \
 SOURCES_TRANPORT_LAYER  :=  $(SOURCES)                      \
 SOURCES_TRANPORT_LAYER  :=  $(SOURCES)                      \
                             src/shellmatta_transport.c      \
                             src/shellmatta_transport.c      \
                             src/shellmatta_crc.c
                             src/shellmatta_crc.c
+AUTH_SOURCES     := $(SOURCES) src/shellmatta_auth.c
+EXAMPLE_SOURCES  := example/main.c
+EXAMPLE_SOURCES  += $(filter-out $(EXAMPLE_SOURCES),$(AUTH_SOURCES))
+EXAMPLE_SOURCES  += $(filter-out $(EXAMPLE_SOURCES),$(SOURCES_TRANPORT_LAYER))
+EXAMPLE_COBJ     := $(patsubst %.c,$(OBJ_DIR_EXAMPLE)%.o,$(EXAMPLE_SOURCES))
+
+CPPCHECK_SOURCES := $(AUTH_SOURCES)                         \
+                    src/shellmatta_transport.c              \
+                    src/shellmatta_crc.c
+CPPCHECK_OPTIONS := --enable=all                                            \
+                    --check-level=exhaustive                                \
+                    --template=gcc                                          \
+                    --cppcheck-build-dir=output/cppcheck                    \
+                    --suppress-xml=cfg/cppcheck/cppcheck_suppressions.xml   \
+                    -DSHELLMATTA_AUTHENTICATION                             \
+                    -Iapi                                                   \
+                    $(CPPCHECK_SOURCES)
 
 
 INCLUDES    := api .
 INCLUDES    := api .
 
 
@@ -63,6 +89,8 @@ INTEGRATIONTEST_SOURCES :=  test/integrationtest/test_main.cpp
 
 
 INTEGRATIONTEST_TRANSPORT_SOURCES  :=  $(INTEGRATIONTEST_SOURCES)                           \
 INTEGRATIONTEST_TRANSPORT_SOURCES  :=  $(INTEGRATIONTEST_SOURCES)                           \
                                        test/integrationtest/test_integration_transport.cpp
                                        test/integrationtest/test_integration_transport.cpp
+INTEGRATIONTEST_AUTH_SOURCES := test/integrationtest_auth/test_main.cpp                 \
+                                test/integrationtest_auth/test_integration_auth.cpp
 
 
 UNITTEST_CPPOBJ  := $(patsubst %.cpp,$(UNITTEST_OBJ_DIR)%.o,$(UNITTEST_SOURCES))
 UNITTEST_CPPOBJ  := $(patsubst %.cpp,$(UNITTEST_OBJ_DIR)%.o,$(UNITTEST_SOURCES))
 
 
@@ -71,32 +99,46 @@ INTEGRATIONTEST_COBJ    :=  $(patsubst %.c,$(INTEGRATIONTEST_CPP_OBJ_DIR)%.o,$(S
 
 
 INTEGRATIONTEST_TRANSPORT_CPPOBJ    :=  $(patsubst %.cpp,$(INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT)%.o,$(INTEGRATIONTEST_TRANSPORT_SOURCES))
 INTEGRATIONTEST_TRANSPORT_CPPOBJ    :=  $(patsubst %.cpp,$(INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT)%.o,$(INTEGRATIONTEST_TRANSPORT_SOURCES))
 INTEGRATIONTEST_TRANSPORT_COBJ     	:=  $(patsubst %.c,$(INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT)%.o,$(SOURCES_TRANPORT_LAYER))
 INTEGRATIONTEST_TRANSPORT_COBJ     	:=  $(patsubst %.c,$(INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT)%.o,$(SOURCES_TRANPORT_LAYER))
+INTEGRATIONTEST_AUTH_CPPOBJ :=  $(patsubst %.cpp,$(INTEGRATIONTEST_AUTH_CPP_OBJ_DIR)%.o,$(INTEGRATIONTEST_AUTH_SOURCES))
+INTEGRATIONTEST_AUTH_COBJ   :=  $(patsubst %.c,$(INTEGRATIONTEST_AUTH_CPP_OBJ_DIR)%.o,$(AUTH_SOURCES))
+
 
 
 CFLAGS                  := $(INCLUDES:%=-I%) -g -Wall -Werror -Wextra -pedantic
 CFLAGS                  := $(INCLUDES:%=-I%) -g -Wall -Werror -Wextra -pedantic
 CFLAGS                  += -DSHELLMATTA_HELP_ALIAS=\(char*\)\"?\"
 CFLAGS                  += -DSHELLMATTA_HELP_ALIAS=\(char*\)\"?\"
+CFLAGS_EXAMPLE          := $(CFLAGS) -DSHELLMATTA_AUTHENTICATION -DSHELLMATTA_TRANSPORT_ENABLE
 TESTFLAGS               := $(CFLAGS) -fprofile-arcs -ftest-coverage
 TESTFLAGS               := $(CFLAGS) -fprofile-arcs -ftest-coverage
+TESTFLAGS_AUTH          := $(CFLAGS) -DSHELLMATTA_AUTHENTICATION -fprofile-arcs -ftest-coverage
 TESTLFLAGS              := -fprofile-arcs -Wl,--allow-multiple-definition
 TESTLFLAGS              := -fprofile-arcs -Wl,--allow-multiple-definition
 TESTFLAGS_TRANSPORT     := $(TESTFLAGS) -DSHELLMATTA_TRANSPORT_ENABLE
 TESTFLAGS_TRANSPORT     := $(TESTFLAGS) -DSHELLMATTA_TRANSPORT_ENABLE
 TESTLFLAGS_TRANSPORT    := $(TESTLFLAGS)
 TESTLFLAGS_TRANSPORT    := $(TESTLFLAGS)
-EXAMPLE_CFLAGS          := $(CFLAGS) -DSHELLMATTA_TRANSPORT_ENABLE
 DEPEND                  = -MT $@ -MF "$(@:%.o=%.d)" -MG -MM
 DEPEND                  = -MT $@ -MF "$(@:%.o=%.d)" -MG -MM
 
 
-EXAMPLE_SOURCES  := example/main.c $(SOURCES_TRANPORT_LAYER)
-EXAMPLE_COBJ     := $(patsubst %.c,$(OBJ_DIR)%.o,$(EXAMPLE_SOURCES))
 
 
+COBJ := $(patsubst %.c,$(OBJ_DIR)%.o,$(SOURCES))
 
 
 EXAMPLE_TARGET                      := $(OBJ_DIR)example/example
 EXAMPLE_TARGET                      := $(OBJ_DIR)example/example
 UNITTEST_TARGET                     := $(OBJ_DIR)test/unittest/unittest
 UNITTEST_TARGET                     := $(OBJ_DIR)test/unittest/unittest
 INTEGRATIONTEST_TARGET              := $(OBJ_DIR)test/integrationtest/integrationtest
 INTEGRATIONTEST_TARGET              := $(OBJ_DIR)test/integrationtest/integrationtest
-INTEGRATIONTEST_TRANSPORT_TARGET    := $(OBJ_DIR)test/integrationtest_transport/integrationtest_transport
-
-OBJ     := $(EXAMPLE_COBJ)                      \
-           $(UNITTEST_CPPOBJ)                   \
-           $(INTEGRATIONTEST_CPPOBJ)            \
-           $(INTEGRATIONTEST_COBJ)              \
-           $(INTEGRATIONTEST_TRANSPORT_CPPOBJ)  \
-           $(INTEGRATIONTEST_TRANSPORT_COBJ)
-DEPS    := $(OBJ:%.o=%.d)
+INTEGRATIONTEST_TARGET_AUTH         := $(OBJ_DIR)test/integrationtest_auth/integrationtest_auth
+INTEGRATIONTEST_TARGET_TRANSPORT    := $(OBJ_DIR)test/integrationtest_transport/integrationtest_transport
+
+TEST_RUN_TARGETS := unittest integrationtest integrationtest_auth integrationtest_transport
+# ensure the tests are running one after another when general test target is called
+ifneq ($(filter test,$(MAKECMDGOALS)),)
+TEST_RUN_TARGETS_HELPER := $(TEST_RUN_TARGETS)
+endif
+TEST_TARGETS := $(UNITTEST_TARGET) $(INTEGRATIONTEST_TARGET) $(INTEGRATIONTEST_TARGET_AUTH) $(INTEGRATIONTEST_TARGET_TRANSPORT)
+
+OBJ     :=  $(COBJ)                             \
+            $(EXAMPLE_COBJ)                     \
+            $(UNITTEST_CPPOBJ)                  \
+            $(INTEGRATIONTEST_CPPOBJ)           \
+            $(INTEGRATIONTEST_COBJ)             \
+            $(INTEGRATIONTEST_AUTH_CPPOBJ)      \
+            $(INTEGRATIONTEST_AUTH_COBJ)        \
+            $(INTEGRATIONTEST_TRANSPORT_CPPOBJ) \
+            $(INTEGRATIONTEST_TRANSPORT_COBJ)
+DEPS    :=  $(OBJ:%.o=%.d)
 
 
 export
 export
 
 
@@ -110,46 +152,34 @@ help:
 	@echo example   - build example
 	@echo example   - build example
 	@echo -----------------------------------------------
 	@echo -----------------------------------------------
 
 
-test: unittest integrationtest integrationtest_transport
-
-cppcheck:
-	- @mkdir -p output/cppcheck/html
-	cppcheck --enable=all --template=gcc --cppcheck-build-dir=output/cppcheck $(SOURCES)
-	cppcheck --enable=all --template=gcc --cppcheck-build-dir=output/cppcheck $(SOURCES) --xml 2>output/cppcheck/cppcheck.xml
-	cppcheck-htmlreport --file=output/cppcheck/cppcheck.xml --title="Shellmatta" --report-dir=output/cppcheck/html
-
-unittest: $(UNITTEST_TARGET)
-	- @mkdir -p output/test/unittest/report
-	@echo running test:
+test: $(TEST_RUN_TARGETS)
+	- @mkdir -p output/test/report
+# run the test alltogether to get coverage from all combined tests
 #	remove coverage from former run
 #	remove coverage from former run
 	@-find . -name "*.gcda" -type f -delete
 	@-find . -name "*.gcda" -type f -delete
-	-$(UNITTEST_TARGET)
+	$(foreach TEST_RUN_TARGET,$(TEST_RUN_TARGETS),./$(OBJ_DIR)test/$(TEST_RUN_TARGET)/$(TEST_RUN_TARGET)${\n})
 
 
 #	remove report from former run
 #	remove report from former run
-	-rm -rf $(OBJ_DIR)test/unittest/report/*
-	gcovr --html-details --output $(OBJ_DIR)test/unittest/report/report.html output/test/unittest -f src -f api -d
+	-rm -rf $(OBJ_DIR)test/report/*
+	gcovr --html-details --output $(OBJ_DIR)test/report/report.html -f src -f api -d
 
 
-integrationtest: $(INTEGRATIONTEST_TARGET)
-	- @mkdir -p output/test/integrationtest/report
-	@echo running test:
-#	remove coverage from former run
-#	@-find . -name "*.gcda" -type f -delete
-	-$(INTEGRATIONTEST_TARGET)
+cppcheck:
+	- @mkdir -p output/cppcheck/html
+	cppcheck $(CPPCHECK_OPTIONS)
+	cppcheck $(CPPCHECK_OPTIONS) --xml 2>output/cppcheck/cppcheck.xml
+	cppcheck-htmlreport --file=output/cppcheck/cppcheck.xml --title="Shellmatta" --report-dir=output/cppcheck/html
 
 
-	#	remove report from former run
-#	-rm -rf $(OBJ_DIR)test/unittest/report/*
-	gcovr --html-details --output $(OBJ_DIR)test/integrationtest/report/report.html output/test/integrationtest -f src -f api -d
 
 
-integrationtest_transport: $(INTEGRATIONTEST_TRANSPORT_TARGET)
-	- @mkdir -p output/test/integrationtest_transport/report
-	@echo running test:
+$(TEST_RUN_TARGETS): $(TEST_TARGETS) $(filter-out $@,$(TEST_RUN_TARGETS_HELPER))
+	- @mkdir -p output/test/$@/report
+	@echo running test $@:
 #	remove coverage from former run
 #	remove coverage from former run
-#	@-find . -name "*.gcda" -type f -delete
-	-$(INTEGRATIONTEST_TRANSPORT_TARGET)
+	@-find . -name "*.gcda" -type f -delete
+	-$(OBJ_DIR)test/$@/$@
 
 
-	#	remove report from former run
-#	-rm -rf $(OBJ_DIR)test/unittest/report/*
-	gcovr --html-details --output $(OBJ_DIR)test/integrationtest_transport/report/report.html output/test/integrationtest_transport -f src -f api -d
+#	remove report from former run
+	-rm -rf $(OBJ_DIR)test/$@/report/*
+	gcovr --html-details --output $(OBJ_DIR)test/$@/report/report.html output/test/$@ -f src -f api -d
 
 
 example: $(EXAMPLE_TARGET)
 example: $(EXAMPLE_TARGET)
 	@echo building example
 	@echo building example
@@ -158,7 +188,7 @@ doc:
 	- @mkdir -p output/doc/html
 	- @mkdir -p output/doc/html
 	- @mkdir -p output/doc/latex
 	- @mkdir -p output/doc/latex
 	doxygen cfg/doxygen/doxyfile
 	doxygen cfg/doxygen/doxyfile
-	
+
 clean:
 clean:
 	- rm -rf $(OBJ_DIR)
 	- rm -rf $(OBJ_DIR)
 
 
@@ -169,48 +199,72 @@ $(EXAMPLE_TARGET): $(EXAMPLE_COBJ)
 $(UNITTEST_TARGET): $(UNITTEST_CPPOBJ)
 $(UNITTEST_TARGET): $(UNITTEST_CPPOBJ)
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
 	$(CPP) $(TESTLFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
 	$(CPP) $(TESTLFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
-	
+
 $(INTEGRATIONTEST_TARGET): $(INTEGRATIONTEST_CPPOBJ) $(INTEGRATIONTEST_COBJ)
 $(INTEGRATIONTEST_TARGET): $(INTEGRATIONTEST_CPPOBJ) $(INTEGRATIONTEST_COBJ)
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
 	$(CPP) $(TESTLFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
 	$(CPP) $(TESTLFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
 	
 	
-$(INTEGRATIONTEST_TRANSPORT_TARGET): $(INTEGRATIONTEST_TRANSPORT_CPPOBJ) $(INTEGRATIONTEST_TRANSPORT_COBJ)
+$(INTEGRATIONTEST_TARGET_TRANSPORT): $(INTEGRATIONTEST_TRANSPORT_CPPOBJ) $(INTEGRATIONTEST_TRANSPORT_COBJ)
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
 	$(CPP) $(TESTLFLAGS_TRANSPORT) $(LIB_PATH) -o $@ $^ $(LIBS)
 	$(CPP) $(TESTLFLAGS_TRANSPORT) $(LIB_PATH) -o $@ $^ $(LIBS)
 
 
+$(INTEGRATIONTEST_TARGET_AUTH): $(INTEGRATIONTEST_AUTH_CPPOBJ) $(INTEGRATIONTEST_AUTH_COBJ)
+	- @mkdir -p $(@D)
+	echo askdjhaskjdhskj
+	$(CPP) $(TESTLFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
+
+$(TARGET): $(OBJ)
+	- @mkdir -p $(@D)
+	$(CC) $(LFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
+
+$(COBJ):
+	- @mkdir -p $(@D)
+	@$(CC) -c $(CFLAGS) $(DEPEND) -o $(@:%.o=%.d) $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
+	$(CC) -c $(CFLAGS) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
+
 $(EXAMPLE_COBJ):
 $(EXAMPLE_COBJ):
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
-	@$(CC) -c $(EXAMPLE_CFLAGS) $(DEPEND) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
-	$(CC) -c $(EXAMPLE_CFLAGS) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
+	@$(CC) -c $(CFLAGS_EXAMPLE) $(DEPEND) -o $(@:%.o=%.d) $(subst $(OBJ_DIR_EXAMPLE), ,$(@:%.o=%.c))
+	$(CC) -c $(CFLAGS_EXAMPLE) -o $@  $(subst $(OBJ_DIR_EXAMPLE), ,$(@:%.o=%.c))
 
 
 $(UNITTEST_CPPOBJ):
 $(UNITTEST_CPPOBJ):
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
-	@$(CPP) -c $(TESTFLAGS) $(DEPEND) -o $@  $(subst $(UNITTEST_OBJ_DIR), ,$(@:%.o=%.cpp))
-	$(CPP) -c $(TESTFLAGS) -o $@  $(subst $(UNITTEST_OBJ_DIR), ,$(@:%.o=%.cpp))
+	@$(CPP) -c $(TESTFLAGS) $(DEPEND) -o $(@:%.o=%.d) $(subst $(UNITTEST_OBJ_DIR), ,$(@:%.o=%.cpp))
+	$(CPP) -c $(TESTFLAGS) -o $@ $(subst $(UNITTEST_OBJ_DIR), ,$(@:%.o=%.cpp))
 
 
 $(INTEGRATIONTEST_CPPOBJ):
 $(INTEGRATIONTEST_CPPOBJ):
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
-	@$(CPP) -c $(TESTFLAGS) $(DEPEND) -o $@  $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR), ,$(@:%.o=%.cpp))
+	@$(CPP) -c $(TESTFLAGS) $(DEPEND) -o $(@:%.o=%.d) $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR), ,$(@:%.o=%.cpp))
 	$(CPP) -c $(TESTFLAGS) -o $@  $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR), ,$(@:%.o=%.cpp))
 	$(CPP) -c $(TESTFLAGS) -o $@  $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR), ,$(@:%.o=%.cpp))
 
 
 $(INTEGRATIONTEST_COBJ):
 $(INTEGRATIONTEST_COBJ):
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
-	@$(CC) -c $(TESTFLAGS) $(DEPEND) -o $@  $(subst $(INTEGRATIONTEST_C_OBJ_DIR), ,$(@:%.o=%.c))
+	@$(CC) -c $(TESTFLAGS) $(DEPEND) -o $(@:%.o=%.d) $(subst $(INTEGRATIONTEST_C_OBJ_DIR), ,$(@:%.o=%.c))
 	$(CC) -c $(TESTFLAGS) -o $@  $(subst $(INTEGRATIONTEST_C_OBJ_DIR), ,$(@:%.o=%.c))
 	$(CC) -c $(TESTFLAGS) -o $@  $(subst $(INTEGRATIONTEST_C_OBJ_DIR), ,$(@:%.o=%.c))
 
 
 $(INTEGRATIONTEST_TRANSPORT_CPPOBJ):
 $(INTEGRATIONTEST_TRANSPORT_CPPOBJ):
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
-	@$(CPP) -c $(TESTFLAGS_TRANSPORT) $(DEPEND) -o $@  $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT), ,$(@:%.o=%.cpp))
+	@$(CPP) -c $(TESTFLAGS_TRANSPORT) $(DEPEND) -o $(@:%.o=%.d) $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT), ,$(@:%.o=%.cpp))
 	$(CPP) -c $(TESTFLAGS_TRANSPORT) -o $@  $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT), ,$(@:%.o=%.cpp))
 	$(CPP) -c $(TESTFLAGS_TRANSPORT) -o $@  $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR_TRANSPORT), ,$(@:%.o=%.cpp))
 
 
 $(INTEGRATIONTEST_TRANSPORT_COBJ):
 $(INTEGRATIONTEST_TRANSPORT_COBJ):
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
-	@$(CC) -c $(TESTFLAGS_TRANSPORT) $(DEPEND) -o $@  $(subst $(INTEGRATIONTEST_C_OBJ_DIR_TRANSPORT), ,$(@:%.o=%.c))
+	@$(CC) -c $(TESTFLAGS_TRANSPORT) $(DEPEND) -o $(@:%.o=%.d) $(subst $(INTEGRATIONTEST_C_OBJ_DIR_TRANSPORT), ,$(@:%.o=%.c))
 	$(CC) -c $(TESTFLAGS_TRANSPORT) -o $@  $(subst $(INTEGRATIONTEST_C_OBJ_DIR_TRANSPORT), ,$(@:%.o=%.c))
 	$(CC) -c $(TESTFLAGS_TRANSPORT) -o $@  $(subst $(INTEGRATIONTEST_C_OBJ_DIR_TRANSPORT), ,$(@:%.o=%.c))
 
 
+$(INTEGRATIONTEST_AUTH_CPPOBJ):
+	- @mkdir -p $(@D)
+	@$(CPP) -c $(TESTFLAGS_AUTH) $(DEPEND) -o $(@:%.o=%.d) $(subst $(INTEGRATIONTEST_AUTH_CPP_OBJ_DIR), ,$(@:%.o=%.cpp))
+	$(CPP) -c $(TESTFLAGS_AUTH) -o $@  $(subst $(INTEGRATIONTEST_AUTH_CPP_OBJ_DIR), ,$(@:%.o=%.cpp))
+
+$(INTEGRATIONTEST_AUTH_COBJ):
+	- @mkdir -p $(@D)
+	$(CC) -c $(TESTFLAGS_AUTH) $(DEPEND) -o $(@:%.o=%.d) $(subst $(INTEGRATIONTEST_AUTH_C_OBJ_DIR), ,$(@:%.o=%.c))
+	$(CC) -c $(TESTFLAGS_AUTH) -o $@  $(subst $(INTEGRATIONTEST_AUTH_C_OBJ_DIR), ,$(@:%.o=%.c))
+
 %.o: %.cpp
 %.o: %.cpp
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
-	@$(CPP) -c $(CFLAGS) $(DEPEND) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
+	@$(CPP) -c $(CFLAGS) $(DEPEND) -o $(@:%.o=%.d) $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
 	$(CPP) -c $(CFLAGS) -o $@  $<
 	$(CPP) -c $(CFLAGS) -o $@  $<
 
 
 -include $(DEPS)
 -include $(DEPS)

+ 70 - 33
src/shellmatta.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -26,6 +26,9 @@
 #ifdef SHELLMATTA_TRANSPORT_ENABLE
 #ifdef SHELLMATTA_TRANSPORT_ENABLE
 #include "shellmatta_transport.h"
 #include "shellmatta_transport.h"
 #endif
 #endif
+#ifdef SHELLMATTA_AUTHENTICATION
+#include "shellmatta_auth.h"
+#endif
 #include <stddef.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdint.h>
 #include <string.h>
 #include <string.h>
@@ -147,13 +150,13 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
             if(0u == inst->hereLength)
             if(0u == inst->hereLength)
             {
             {
                 /**
                 /**
-                    *  @dot
-                    *  digraph heredocParser {
-                    *      start -> wait [ label="<< in first line - store delimiter" ];
-                    *      wait -> wait [ label="delimiter not detected" ];
-                    *      wait -> end [ label="delimiter found - remove all heredoc stuff and send the input to the command" ];
-                    *  }
-                    *  @enddot */
+                     *  @dot
+                     *  digraph heredocParser {
+                     *      start -> wait [ label="<< in first line - store delimiter" ];
+                     *      wait -> wait [ label="delimiter not detected" ];
+                     *      wait -> end [ label="delimiter found - remove all heredoc stuff and send the input to the command" ];
+                     *  }
+                     *  @enddot */
 
 
                 /** -# check for heredoc - add string delimiter to stop strstr from searching too far */
                 /** -# check for heredoc - add string delimiter to stop strstr from searching too far */
                 inst->buffer[inst->inputCount] = '\0';
                 inst->buffer[inst->inputCount] = '\0';
@@ -279,14 +282,25 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
                     {
                     {
                         utils_writeEcho(inst, "\r\n", 2u);
                         utils_writeEcho(inst, "\r\n", 2u);
                         shellmatta_opt_init(inst, cmdLen + 1u);
                         shellmatta_opt_init(inst, cmdLen + 1u);
+
+#ifdef SHELLMATTA_AUTHENTICATION
+                        cmdRet = SHELLMATTA_OK;
+                        if (SHELLMATTA_OK == shellmatta_auth_is_cmd_permitted(inst, cmd))
+                        {
+                            cmdExecuted = 1u;
+                            cmdRet = cmd->cmdFct(handle, inst->buffer, inst->inputCount);
+                        }
+
+#else
                         cmdExecuted = 1u;
                         cmdExecuted = 1u;
                         cmdRet = cmd->cmdFct(handle, inst->buffer, inst->inputCount);
                         cmdRet = cmd->cmdFct(handle, inst->buffer, inst->inputCount);
+#endif
 
 
                         switch(cmdRet)
                         switch(cmdRet)
                         {
                         {
                             case SHELLMATTA_CONTINUE:
                             case SHELLMATTA_CONTINUE:
                                 /** -# initialize stdin buffer and continuous cmd */
                                 /** -# initialize stdin buffer and continuous cmd */
-                                inst->stdinIdx      = inst->inputCount + 1u;
+                                inst->stdinIdx      = inst->bufferSize - 2u;
                                 inst->stdinLength   = 0u;
                                 inst->stdinLength   = 0u;
                                 inst->continuousCmd = cmd;
                                 inst->continuousCmd = cmd;
                                 ret                 = cmdRet;
                                 ret                 = cmdRet;
@@ -311,9 +325,16 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
 
 
                 if ((0u == cmdExecuted) && (inst->inputCount > 0))
                 if ((0u == cmdExecuted) && (inst->inputCount > 0))
                 {
                 {
-                    SHELLMATTA_WRITE("\r\nCommand: ", 11u);
-                    SHELLMATTA_WRITE(inst->buffer, inst->inputCount);
-                    SHELLMATTA_WRITE(" not found", 10u);
+                    if (inst->echoEnabled)
+                    {
+                        inst->write("\r\nCommand: ", 11u);
+                        inst->write(inst->buffer, inst->inputCount);
+                        inst->write(" not found", 10u);
+                    }
+                    else
+                    {
+                        inst->write("\r\nCommand not found!", 20u);
+                    }
                 }
                 }
 
 
                 /** -# terminate this session if no continuous mode is requested */
                 /** -# terminate this session if no continuous mode is requested */
@@ -325,7 +346,7 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
             }
             }
         }
         }
         /** -# ignore newline as first character (to be compatible to
         /** -# ignore newline as first character (to be compatible to
-            * terminals sending newline after return) */
+         * terminals sending newline after return */
         else if((0u == inst->inputCount) && ('\n' == data[inst->byteCounter]))
         else if((0u == inst->inputCount) && ('\n' == data[inst->byteCounter]))
         {
         {
             /* do nothing */
             /* do nothing */
@@ -337,7 +358,7 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
             autocomplete_run(inst);
             autocomplete_run(inst);
         }
         }
         /** -# check for cancel -
         /** -# check for cancel -
-            *      terminate current input and print prompt again */
+         *      terminate current input and print prompt again */
         else if('\x03' == data[inst->byteCounter])
         else if('\x03' == data[inst->byteCounter])
         {
         {
             inst->dirty = false;
             inst->dirty = false;
@@ -421,44 +442,39 @@ shellmatta_retCode_t shellmatta_doInit(
         &&  (NULL != writeFct)
         &&  (NULL != writeFct)
         &&  ((NULL != historyBuffer) || (0u == historyBufferSize)))
         &&  ((NULL != historyBuffer) || (0u == historyBufferSize)))
     {
     {
+        /** -# clear the shellmatta instance */
+        memset((void *)inst, 0, sizeof(shellmatta_instance_t));
+
         /** -# copy all provided buffers into the shellmatta instance */
         /** -# copy all provided buffers into the shellmatta instance */
         inst->buffer                = buffer;
         inst->buffer                = buffer;
         inst->bufferSize            = bufferSize;
         inst->bufferSize            = bufferSize;
-        inst->inputCount            = 0u;
-        inst->byteCounter           = 0u;
-        inst->lastNewlineIdx        = 0u;
-        inst->cursor                = 0u;
-        inst->stdinIdx              = 0u;
-        inst->stdinLength           = 0u;
         inst->historyBuffer         = historyBuffer;
         inst->historyBuffer         = historyBuffer;
         inst->historyBufferSize     = historyBufferSize;
         inst->historyBufferSize     = historyBufferSize;
-        inst->historyStart          = 0u;
-        inst->historyEnd            = 0u;
-        inst->historyRead           = 0u;
         inst->historyReadUp         = true;
         inst->historyReadUp         = true;
         inst->write                 = writeFct;
         inst->write                 = writeFct;
         inst->prompt                = prompt;
         inst->prompt                = prompt;
         inst->echoEnabled           = true;
         inst->echoEnabled           = true;
         inst->dirty                 = false;
         inst->dirty                 = false;
-        inst->tabCounter            = 0u;
-        inst->escapeCounter         = 0u;
-        inst->hereStartIdx          = 0u;
-        inst->hereDelimiterIdx      = 0u;
-        inst->hereLength            = 0u;
         inst->delimiter             = '\r';
         inst->delimiter             = '\r';
         inst->mode                  = SHELLMATTA_MODE_INSERT;
         inst->mode                  = SHELLMATTA_MODE_INSERT;
         inst->cmdList               = &(inst->helpCmd);
         inst->cmdList               = &(inst->helpCmd);
-        inst->continuousCmd         = NULL;
-        inst->busyCmd               = NULL;
-        inst->cmdListIsConst        = false;
         shellmatta_opt_init(inst, 0u);
         shellmatta_opt_init(inst, 0u);
 
 
         /** -# copy the help command structure to this instance */
         /** -# copy the help command structure to this instance */
         memcpy(&(inst->helpCmd), &helpCmd, sizeof(shellmatta_cmd_t));
         memcpy(&(inst->helpCmd), &helpCmd, sizeof(shellmatta_cmd_t));
+#ifdef SHELLMATTA_AUTHENTICATION
+        /** -# copy the auth commands to the instance */
+        memcpy(&(inst->loginCmd), &shellmatta_auth_loginCmd, sizeof(shellmatta_cmd_t));
+        memcpy(&(inst->logoutCmd), &shellmatta_auth_logoutCmd, sizeof(shellmatta_cmd_t));
+#endif
 
 
         if(NULL != cmdList)
         if(NULL != cmdList)
         {
         {
+#ifndef SHELLMATTA_AUTHENTICATION
             inst->helpCmd.next = (shellmatta_cmd_t *) cmdList;
             inst->helpCmd.next = (shellmatta_cmd_t *) cmdList;
+#else
+            inst->logoutCmd.next = (shellmatta_cmd_t *) cmdList;
+#endif
             inst->cmdListIsConst = true;
             inst->cmdListIsConst = true;
         }
         }
 
 
@@ -488,7 +504,7 @@ shellmatta_retCode_t shellmatta_doInit(
  * It resets all internal states - the buffers are left as they are - they will be overwritten.
  * It resets all internal states - the buffers are left as they are - they will be overwritten.
  * The history buffer is deleted as well.
  * The history buffer is deleted as well.
  */
  */
-shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle, bool printPrompt)
+shellmatta_retCode_t shellmatta_resetShell(shellmatta_handle_t handle, bool printPrompt)
 {
 {
     shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
     shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
     shellmatta_retCode_t ret = SHELLMATTA_OK;
     shellmatta_retCode_t ret = SHELLMATTA_OK;
@@ -517,6 +533,11 @@ shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle, bool pri
         inst->hereLength            = 0u;
         inst->hereLength            = 0u;
         shellmatta_opt_init(inst, 0u);
         shellmatta_opt_init(inst, 0u);
 
 
+#ifdef SHELLMATTA_AUTHENTICATION
+        inst->userId = 0u;
+        inst->userPointer = NULL;
+#endif
+
         if(true == printPrompt)
         if(true == printPrompt)
         {
         {
             /** -# print a prompt if requested */
             /** -# print a prompt if requested */
@@ -608,6 +629,22 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
                 tempCmd = tempCmd->next;
                 tempCmd = tempCmd->next;
             }
             }
         }
         }
+
+#ifdef SHELLMATTA_AUTHENTICATION
+        /** -# append permissions to added command if any */
+        if (inst->permList)
+        {
+            /** -# Search for command in perm list */
+            for (uint32_t i = 0u; i < inst->permListLength; i++)
+            {
+                if (0 == strcmp(cmd->cmd, inst->permList[i].cmd))
+                {
+                    cmd->authLink = &inst->permList[i];
+                    break;
+                }
+            }
+        }
+#endif
     }
     }
     else
     else
     {
     {
@@ -624,7 +661,7 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
  * @return      errorcode   #SHELLMATTA_OK
  * @return      errorcode   #SHELLMATTA_OK
  *                          #SHELLMATTA_USE_FAULT (param err)
  *                          #SHELLMATTA_USE_FAULT (param err)
  */
  */
-shellmatta_retCode_t shellmatta_removeCmd(shellmatta_handle_t handle, shellmatta_cmd_t *cmd)
+shellmatta_retCode_t shellmatta_removeCmd(shellmatta_handle_t handle, const shellmatta_cmd_t *cmd)
 {
 {
     shellmatta_instance_t   *inst       = (shellmatta_instance_t*)handle;
     shellmatta_instance_t   *inst       = (shellmatta_instance_t*)handle;
     shellmatta_cmd_t       *prevCmd;
     shellmatta_cmd_t       *prevCmd;

+ 634 - 0
src/shellmatta_auth.c

@@ -0,0 +1,634 @@
+/*
+ * Copyright (c) 2022 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    shellmatta_auth.c
+ * @brief   simple authentication method
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_auth
+ * @{
+ */
+
+#include "shellmatta.h"
+#include "shellmatta_auth.h"
+#include "shellmatta_history.h"
+#include "shellmatta_utils.h"
+#include "shellmatta_escape.h"
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+
+/**
+ * @brief       wraps the input to the login command and mimics the editability of the standard shell input
+ * @param[in, out]  inst    shellmatta instance to work with
+ * @param[in]       data    one byte of data the user put in
+ * @param[in]       hide    true: do not print anything tho the output (e.g. password input)
+ * @return      #SHELLMATTA_BUSY    => reading in data
+ *              #SHELLMATTA_OK      => delimiter (e.g. enter) detected
+ */
+shellmatta_retCode_t inputWrapper(shellmatta_instance_t *inst, char data, bool hide)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_BUSY;
+    bool echoEnabledBackup = inst->echoEnabled;
+
+    /** -# disable echo when hidden */
+    inst->echoEnabled &= !hide;
+
+    /** -# handle escape sequences */
+    if(inst->escapeCounter != 0u)
+    {
+        escape_handleSequence(inst, data);
+    }
+    else if (inst->delimiter == data)
+    {
+        ret = SHELLMATTA_OK;
+    }
+        /** -# ignore newline as first character (to be compatible to
+     * terminals sending newline after return */
+    else if((0u == inst->inputCount) && ('\n' == data))
+    {
+        /* do nothing */
+    }
+    /** -# check for backspace */
+    else if(    ('\b'   == data)
+            ||  ('\x7f' == data))
+    {
+        utils_removeChars(inst, 1u, true);
+    }
+    /** -# check for start of escape sequence */
+    else if('\x1b' == data)
+    {
+        inst->escapeCounter = 1u;
+    }
+    else
+    {
+        utils_insertChars(inst, &data, 1u);
+    }
+
+    inst->echoEnabled = echoEnabledBackup;
+
+    return ret;
+}
+
+/**
+ * @brief       searches the user list for a passed username and returns the userId
+ * @param[in]   inst        shellmatta instance to work with
+ * @param[in]   username    username to search for - pointer to string
+ * @return      userId or 0 if user was not found
+ */
+static uint32_t getUserIdFromName(shellmatta_instance_t *inst, const char *username)
+{
+    uint32_t    userId  = 0u;
+    uint32_t    i;
+
+    for (i = 0u; i < inst->userListLength; i++)
+    {
+        /** -# search for user */
+        if (0 == strcmp(inst->userList[i].username, username))
+        {
+            userId = inst->userList[i].userId;
+            break;
+        }
+    }
+
+    return userId;
+}
+
+/**
+ * @brief       checks the passed password and logs the user into the instance on success
+ * @param[in, out]  handle      shellmatta handle to work with
+ * @param[in]       userId      userId to check the password for
+ * @param[in]       password    password to check for - pointer to string
+ */
+void checkPassword(shellmatta_handle_t handle, uint32_t userId, char *password)
+{
+    shellmatta_instance_t   *inst   = (shellmatta_instance_t*)handle;
+    uint32_t                approvedUserId  = 0u;
+    uint32_t                i;
+
+    if (NULL != inst->checkFct)
+    {
+        approvedUserId = inst->checkFct(userId, password) == SHELLMATTA_OK ? userId : 0u;
+    }
+    else
+    {
+        for (i = 0u; i < inst->userListLength; i++)
+        {
+            /** -# search for user */
+            if (inst->userList[i].userId == userId)
+            {
+                /** -# check password */
+                if (0 == strcmp(inst->userList[i].password, password))
+                {
+                    approvedUserId = inst->userList[i].userId;
+                }
+                break;
+            }
+        }
+    }
+
+    /** -# call log function - only for unsuccessful logins - successful logins are handled by #shellmatta_auth_login */
+    if ((inst->logFct) && (0u == approvedUserId))
+    {
+        inst->logFct(userId, SHELLMATTA_AUTH_EVENT_LOGIN_FAILED);
+    }
+
+    /** -# print login result */
+    if (0 == approvedUserId)
+    {
+        shellmatta_write(handle, "username or password is wrong\r\n", 31);
+    }
+    else
+    {
+        shellmatta_write(handle, "login successful\r\n", 18);
+        (void)shellmatta_auth_login(handle, approvedUserId);
+    }
+}
+
+/**
+ * @brief       handles the login based on username and password
+ * @param[in]   handle      handle  shellmatta instance handle
+ * @param[in]   arguments   arguments containing a command name or alias
+ * @param[in]   length      length of the arguments
+ * @return      #SHELLMATTA_OK
+ *              #SHELLMATTA_CONTINUE (waiting for input)
+  */
+static shellmatta_retCode_t loginCmdFct(const shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    shellmatta_instance_t   *inst   = (shellmatta_instance_t*)handle;
+    shellmatta_retCode_t    ret     = SHELLMATTA_OK;
+    char option;
+    char *argument;
+    uint32_t argLen;
+    char *username = NULL;
+    char *password = NULL;
+
+    static const shellmatta_opt_long_t options[] =
+    {
+        {"username" , 'u',  SHELLMATTA_OPT_ARG_REQUIRED},
+        {"password" , 'p',  SHELLMATTA_OPT_ARG_REQUIRED},
+        {NULL       , '\0', SHELLMATTA_OPT_ARG_NONE}
+    };
+
+    /** -# handle the login state machine */
+    switch(inst->loginState)
+    {
+        /** -# init the login procedure - use directly passed credentials if any */
+        case SHELLMATTA_AUTH_IDLE:
+            ret = shellmatta_opt_long(handle, options, &option, &argument, &argLen);
+            while(SHELLMATTA_OK == ret)
+            {
+                switch(option)
+                {
+                    case 'u':
+                        if(NULL != argument)
+                        {
+                            username = argument;
+                        }
+                        break;
+                    case 'p':
+                        if(NULL != argument)
+                        {
+                            password = argument;
+                        }
+                        break;
+                    default:
+                        shellmatta_write(handle, "Unknown option\r\n", 16);
+                        return SHELLMATTA_USE_FAULT;
+                        break;
+                }
+                ret = shellmatta_opt_long(handle, options, &option, &argument, &argLen);
+            }
+
+            if ((NULL != username) && (NULL != password))
+            {
+                inst->tmpUserId = getUserIdFromName(inst, username);
+                checkPassword(handle, inst->tmpUserId, password);
+            }
+            /** -# user passed username and start interactive password input */
+            else if(NULL != username)
+            {
+                inst->inputCount = 0u;
+                inst->cursor = 0u;
+
+                inst->tmpUserId = getUserIdFromName(handle, username);
+                shellmatta_write(handle, "\r\nenter password:\r\n", 19);
+                inst->loginState = SHELLMATTA_AUTH_PASSWORD;
+                ret = SHELLMATTA_CONTINUE;
+            }
+            else if (NULL != password)
+            {
+                shellmatta_write(handle, "Missing username\r\n", 18);
+                ret = SHELLMATTA_USE_FAULT;
+            }
+            else
+            {
+                /** -# no credentials are passed with the command - start the interactive input */
+                inst->inputCount = 0u;
+                inst->cursor = 0u;
+                shellmatta_write(handle, "enter username:\r\n", 17);
+                inst->loginState = SHELLMATTA_AUTH_USERNAME;
+                ret = SHELLMATTA_CONTINUE;
+            }
+            break;
+
+        case SHELLMATTA_AUTH_USERNAME:
+            /** -# read in password characters to the instance buffer */
+            (void)shellmatta_read(handle, &argument, &argLen);
+            if ((1 == argLen) && (SHELLMATTA_OK == inputWrapper(inst, argument[0], false)))
+            {
+                /** -# store user id */
+                inst->buffer[inst->inputCount] = '\0';
+                inst->tmpUserId = getUserIdFromName(handle, inst->buffer);
+
+                /** -# reinitialize input */
+                inst->inputCount = 0u;
+                inst->cursor = 0u;
+
+                shellmatta_write(handle, "\r\nenter password:\r\n", 19);
+                inst->loginState = SHELLMATTA_AUTH_PASSWORD;
+            }
+            ret = SHELLMATTA_CONTINUE;
+            break;
+
+        case SHELLMATTA_AUTH_PASSWORD:
+            /** -# read in password characters to the instance buffer - hiding output */
+            (void)shellmatta_read(handle, &argument, &argLen);
+            if ((1 == argLen) && (SHELLMATTA_OK == inputWrapper(inst, argument[0], true)))
+            {
+                /** -# check input username and password */
+                inst->buffer[inst->inputCount] = '\0';
+                shellmatta_write(handle, "\r\n", 2);
+                checkPassword(handle, inst->tmpUserId, inst->buffer);
+                inst->loginState = SHELLMATTA_AUTH_IDLE;
+            }
+            else
+            {
+                ret = SHELLMATTA_CONTINUE;
+            }
+            break;
+        default:
+            break;
+    }
+
+    (void)arguments;
+    (void)length;
+
+    return ret;
+}
+const shellmatta_cmd_t shellmatta_auth_loginCmd = {"login"
+                                                 , "li"
+                                                 , "Login command"
+                                                 , "login interactively or use -u <username> -p <password>"
+                                                 , loginCmdFct
+                                                 , NULL
+                                                 , NULL};
+
+
+/**
+ * @brief       handles the logout of the current session
+ * @param[in]   handle      handle  shellmatta instance handle
+ * @param[in]   arguments   arguments containing a command name or alias
+ * @param[in]   length      length of the arguments
+ * @return      #SHELLMATTA_OK
+ */
+static shellmatta_retCode_t logoutCmdFct(const shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
+
+    /** -# log the logout event */
+    if (inst->logFct)
+    {
+        inst->logFct(inst->userId, SHELLMATTA_AUTH_EVENT_LOGOUT);
+    }
+
+    shellmatta_auth_logout(handle);
+    shellmatta_write(handle, "good bye\r\n", 10);
+
+    (void)arguments;
+    (void)length;
+
+    return SHELLMATTA_OK;
+}
+const shellmatta_cmd_t shellmatta_auth_logoutCmd = {"logout"
+                                                  , "lo"
+                                                  , "Logout command"
+                                                  , "logs out the currently logged in user"
+                                                  , logoutCmdFct
+                                                  , NULL
+                                                  , NULL};
+
+/**
+ * @brief       initializes the shellmatta authentication system
+ * @param[in]   handle          shellmatta instance handle
+ * @param[in]   userList        list to specify all users pointer to a list of type shellmatta_user_t
+ * @param[in]   userListLength  length of the user list
+ * @param[in]   permList        list to specify the command permissions pointer to a list of type shellmatta_perm_t
+ * @param[in]   permListLength  length of the perm list
+ * @param[in]   customLogin     true: no internal login command is provided
+ * @param[in]   checkFct        pointer to custom credential check function of type shellmatta_check_t
+ * @param[in]   logFct          pointer to login log function of type shellmatta_auth_log_t
+ * @return      errorcode   #SHELLMATTA_OK
+ *                          #SHELLMATTA_DUPLICATE (login/logout commands already existed)
+ *                          #SHELLMATTA_USE_FAULT (param err)
+ */
+shellmatta_retCode_t shellmatta_auth_init(shellmatta_handle_t       handle,
+                                          shellmatta_auth_user_t    *userList,
+                                          uint32_t                  userListLength,
+                                          shellmatta_auth_perm_t    *permList,
+                                          uint32_t                  permListLength,
+                                          bool                      customLogin,
+                                          shellmatta_auth_check_t   checkFct,
+                                          shellmatta_auth_log_t     logFct)
+{
+    shellmatta_retCode_t    ret     = SHELLMATTA_OK;
+    shellmatta_instance_t   *inst   = (shellmatta_instance_t*)handle;
+    uint32_t                i;
+    shellmatta_cmd_t        *cmd;
+
+    /** -# check parameters for plausibility  */
+    if(     (NULL               != inst)
+        &&  (SHELLMATTA_MAGIC   == inst->magic)
+        &&  (NULL               != userList)
+        &&  (0                  != userListLength)
+        &&  (NULL               != permList)
+        &&  (0                  != permListLength))
+    {
+        inst->loginState = SHELLMATTA_AUTH_IDLE;
+        inst->userId = 0u;
+        inst->tmpUserId = 0u;
+        inst->userPointer = NULL;
+        inst->userList = userList;
+        inst->userListLength = userListLength;
+        inst->permList = permList;
+        inst->permListLength = permListLength;
+        SHELLMATTA_RET(ret, shellmatta_addCmd(handle, &inst->logoutCmd));
+        if(!customLogin)
+        {
+            SHELLMATTA_RET(ret, shellmatta_addCmd(handle, &inst->loginCmd));
+        }
+        inst->checkFct  = checkFct;
+        inst->logFct    = logFct;
+
+        /** -# assign links to the perm list to each command */
+        cmd = inst->cmdList;
+
+        /** -# search for a matching command */
+        while (NULL != cmd)
+        {
+            /** -# Search for command in perm list */
+            for (i = 0u; i < permListLength; i++)
+            {
+                if (0 == strcmp(cmd->cmd, permList[i].cmd))
+                {
+                    cmd->authLink = &permList[i];
+                    break;
+                }
+            }
+
+            cmd = cmd->next;
+        }
+    }
+    else
+    {
+        ret = SHELLMATTA_USE_FAULT;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief       logs a passed user id into the shellmatta instance
+ * @param[in]   handle      shellmatta instance handle
+ * @param[in]   userId      userId to login
+ * @return      errorcode   #SHELLMATTA_OK
+ *                          #SHELLMATTA_ERR (user does not exist)
+ *                          #SHELLMATTA_USE_FAULT (param err)
+ */
+shellmatta_retCode_t shellmatta_auth_login(shellmatta_handle_t handle, uint32_t userId)
+{
+    shellmatta_retCode_t    ret     = SHELLMATTA_OK;
+    shellmatta_instance_t   *inst   = (shellmatta_instance_t*)handle;
+    uint32_t                i;
+
+    /** -# check parameters for plausibility  */
+    if(     (NULL               != inst)
+        &&  (SHELLMATTA_MAGIC   == inst->magic))
+    {
+        /** -# log passed user id into the instance */
+        inst->userId = userId;
+        inst->userPointer = NULL;
+        if (0 != userId)
+        {
+            /** -# set user pointer to print the name in the prompt */
+            for (i = 0u; i < inst->userListLength; i++)
+            {
+                if (inst->userList[i].userId == userId)
+                {
+                    inst->userPointer = &inst->userList[i];
+                    break;
+                }
+            }
+        }
+
+        /** -# check if the user did exist */
+        if (NULL == inst->userPointer)
+        {
+            ret = SHELLMATTA_ERROR;
+        }
+
+        /** -# call log function */
+        if (inst->logFct)
+        {
+            inst->logFct(userId,
+                         NULL != inst->userPointer ?                \
+                                 SHELLMATTA_AUTH_EVENT_LOGIN :      \
+                                 SHELLMATTA_AUTH_EVENT_LOGIN_FAILED);
+        }
+    }
+    else
+    {
+        ret = SHELLMATTA_USE_FAULT;
+    }
+    return ret;
+}
+
+/**
+ * @brief       logs the currently logged in user out of the instance
+ * @param[in]   handle      shellmatta instance handle
+ * @return      errorcode   #SHELLMATTA_OK
+ *                          #SHELLMATTA_USE_FAULT (param err)
+ */
+shellmatta_retCode_t shellmatta_auth_logout(shellmatta_handle_t handle)
+{
+    shellmatta_retCode_t    ret     = SHELLMATTA_OK;
+    shellmatta_instance_t   *inst   = (shellmatta_instance_t*)handle;
+
+    /** -# check parameters for plausibility  */
+    if(     (NULL               != inst)
+        &&  (SHELLMATTA_MAGIC   == inst->magic))
+    {
+        inst->userId = 0u;
+        inst->userPointer = NULL;
+
+        /** -# clear the history buffer */
+        (void)history_clear(handle);
+    }
+    else
+    {
+        ret = SHELLMATTA_USE_FAULT;
+    }
+    return ret;
+}
+
+/**
+ * @brief       returns the currently logged in userId
+ * @param[in]   handle      shellmatta instance handle
+ * @return      userId or 0 if no user is logged in
+ */
+uint32_t shellmatta_auth_getLoggedInUserId(shellmatta_handle_t handle)
+{
+    uint32_t                    userId  = 0u;
+    const shellmatta_instance_t *inst   = (shellmatta_instance_t*)handle;
+
+    /** -# check parameters for plausibility  */
+    if(     (NULL               != inst)
+        &&  (SHELLMATTA_MAGIC   == inst->magic))
+    {
+        userId = inst->userId;
+    }
+
+    return userId;
+}
+
+/**
+ * @brief           copies the username into the passed buffer
+ * @param[in]       handle      shellmatta instance handle
+ * @param[out]      data        pointer to buffer to write the username into
+ * @param[in, out]  length      in - length of the passed buffer, out - actual stringlength of the username
+ * @return          errorcode   #SHELLMATTA_OK
+ *                              #SHELLMATTA_ERROR (passed buffer is too small)
+ *                              #SHELLMATTA_USE_FAULT (param err)
+ */
+shellmatta_retCode_t shellmatta_auth_getLoggedInUserName(shellmatta_handle_t handle, char *data, uint32_t *length)
+{
+    shellmatta_retCode_t    ret     = SHELLMATTA_OK;
+    shellmatta_instance_t   *inst   = (shellmatta_instance_t*)handle;
+    size_t                  userNameLength;
+
+    /** -# check parameters for plausibility  */
+    if(     (NULL               != inst)
+        &&  (SHELLMATTA_MAGIC   == inst->magic)
+        &&  (data               != NULL)
+        &&  (length             != NULL))
+    {
+        if (NULL != inst->userPointer)
+        {
+            userNameLength = strlen(inst->userPointer->username);
+
+            if (userNameLength < *length)
+            {
+                (void)strcpy(data, inst->userPointer->username);
+                *length = userNameLength;
+            }
+            else
+            {
+                ret = SHELLMATTA_ERROR;
+            }
+        }
+    }
+    else
+    {
+        ret = SHELLMATTA_USE_FAULT;
+    }
+    return ret;
+}
+
+/**
+ * @brief           changes the password of the passed username
+ * @param[in]       handle      shellmatta instance handle
+ * @param[in]       username    username to change the password of
+ * @param[in]       password    password to be set for the user
+ * @return          errorcode   #SHELLMATTA_OK
+ *                              #SHELLMATTA_ERROR (user not found)
+ *                              #SHELLMATTA_USE_FAULT (param err)
+ */
+shellmatta_retCode_t shellmatta_auth_chpasswd(shellmatta_handle_t handle, const char *username, const char *password)
+{
+    shellmatta_retCode_t    ret     = SHELLMATTA_ERROR;
+    shellmatta_instance_t   *inst   = (shellmatta_instance_t*)handle;
+    uint32_t                i;
+
+    /** -# check parameters for plausibility  */
+    if(     (NULL               != inst)
+        &&  (SHELLMATTA_MAGIC   == inst->magic)
+        &&  (username           != NULL)
+        &&  (password           != NULL))
+    {
+        for (i = 0u; i < inst->userListLength; i++)
+        {
+            /** -# search for user */
+            if (0 == strcmp(inst->userList[i].username, username))
+            {
+                inst->userList[i].password = password;
+                return SHELLMATTA_OK;
+            }
+        }
+    }
+    else
+    {
+        ret = SHELLMATTA_USE_FAULT;
+    }
+    return ret;
+}
+
+/**
+ * @brief       checks if a command is accessible by the logged in user
+ * @param[in]   inst    shellmatta instance to work with
+ * @param[in]   cmd     command to check permissions for
+ * @return      errorcode   #SHELLMATTA_OK      => permitted
+ *                          #SHELLMATTA_ERROR   => not permitted
+ */
+shellmatta_retCode_t shellmatta_auth_is_cmd_permitted(const shellmatta_instance_t *inst, shellmatta_cmd_t *cmd)
+{
+    shellmatta_retCode_t    ret     = SHELLMATTA_ERROR;
+    shellmatta_auth_perm_t  *permList;
+    uint32_t                i;
+
+    /**! -# commands without an authentication set are public */
+    if (NULL == cmd->authLink)
+    {
+        ret = SHELLMATTA_OK;
+    }
+    /**! -# allow superuser to access all commands */
+    else if ((NULL != inst->userPointer) && (true == inst->userPointer->superuser))
+    {
+        ret = SHELLMATTA_OK;
+    }
+    else
+    {
+        permList = cmd->authLink;
+        for (i = 0u; i < permList->userIdslength; i++)
+        {
+            if (inst->userId == permList->userIds[i])
+            {
+                ret = SHELLMATTA_OK;
+                break;
+            }
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @}
+ */

+ 38 - 0
src/shellmatta_auth.h

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2022 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    shellmatta_auth.c
+ * @brief   simple authentication method
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_auth
+ * @{
+ */
+#ifndef _SHELLMATTA_AUTH_H_
+#define _SHELLMATTA_AUTH_H_
+
+#include "shellmatta.h"
+#include <stdint.h>
+
+/** @brief login command to enable a user to login */
+extern const shellmatta_cmd_t shellmatta_auth_loginCmd;
+
+/** @brief logout command */
+extern const shellmatta_cmd_t shellmatta_auth_logoutCmd;
+
+
+shellmatta_retCode_t shellmatta_auth_is_cmd_permitted(const shellmatta_instance_t   *inst,
+                                                      shellmatta_cmd_t              *cmd);
+
+#endif
+
+/** @} */
+

+ 25 - 1
src/shellmatta_autocomplete.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -20,6 +20,9 @@
 #include "shellmatta.h"
 #include "shellmatta.h"
 #include "shellmatta_autocomplete.h"
 #include "shellmatta_autocomplete.h"
 #include "shellmatta_utils.h"
 #include "shellmatta_utils.h"
+#ifdef SHELLMATTA_AUTHENTICATION
+#include "shellmatta_auth.h"
+#endif
 #include <stdint.h>
 #include <stdint.h>
 #include <string.h>
 #include <string.h>
 
 
@@ -50,6 +53,13 @@ void autocomplete_run(shellmatta_instance_t *inst)
         /** -# loop through all registered commands */
         /** -# loop through all registered commands */
         while (NULL != cmd)
         while (NULL != cmd)
         {
         {
+#ifdef SHELLMATTA_AUTHENTICATION
+            if (SHELLMATTA_OK != shellmatta_auth_is_cmd_permitted(inst, cmd))
+            {
+                cmd = cmd->next;
+                continue;
+            }
+#endif
             /** -# check if command matches the input */
             /** -# check if command matches the input */
             if(    (strlen(cmd->cmd) >= inst->cursor)
             if(    (strlen(cmd->cmd) >= inst->cursor)
                 && (0 == strncmp(cmd->cmd, inst->buffer, inst->cursor)))
                 && (0 == strncmp(cmd->cmd, inst->buffer, inst->cursor)))
@@ -85,6 +95,13 @@ void autocomplete_run(shellmatta_instance_t *inst)
         if(printedLen > 0u)
         if(printedLen > 0u)
         {
         {
             utils_writeEcho(inst, "\r\n", 2u);
             utils_writeEcho(inst, "\r\n", 2u);
+#ifdef SHELLMATTA_AUTHENTICATION
+            if (NULL != inst->userPointer)
+            {
+                utils_writeEcho(inst, inst->userPointer->username, strlen(inst->userPointer->username));
+                utils_writeEcho(inst, "@", 1);
+            }
+#endif
             utils_writeEcho(inst, inst->prompt, strlen(inst->prompt));
             utils_writeEcho(inst, inst->prompt, strlen(inst->prompt));
             utils_writeEcho(inst, inst->buffer, inst->inputCount);
             utils_writeEcho(inst, inst->buffer, inst->inputCount);
             tempCursor = inst->cursor;
             tempCursor = inst->cursor;
@@ -98,6 +115,13 @@ void autocomplete_run(shellmatta_instance_t *inst)
         /** -# loop through all registered commands */
         /** -# loop through all registered commands */
         while (NULL != cmd)
         while (NULL != cmd)
         {
         {
+#ifdef SHELLMATTA_AUTHENTICATION
+            if (SHELLMATTA_OK != shellmatta_auth_is_cmd_permitted(inst, cmd))
+            {
+                cmd = cmd->next;
+                continue;
+            }
+#endif
             /** -# check if command matches the input */
             /** -# check if command matches the input */
             if(    (strlen(cmd->cmd) >= inst->cursor)
             if(    (strlen(cmd->cmd) >= inst->cursor)
                 && (0 == strncmp(cmd->cmd, inst->buffer, inst->cursor)))
                 && (0 == strncmp(cmd->cmd, inst->buffer, inst->cursor)))

+ 1 - 1
src/shellmatta_autocomplete.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this

+ 1 - 1
src/shellmatta_escape.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this

+ 1 - 1
src/shellmatta_escape.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this

+ 16 - 3
src/shellmatta_history.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -16,7 +16,7 @@
  * @addtogroup shellmatta_history
  * @addtogroup shellmatta_history
  * @{
  * @{
  */
  */
-
+#include <string.h>
 #include "shellmatta_history.h"
 #include "shellmatta_history.h"
 #include "shellmatta.h"
 #include "shellmatta.h"
 #include "shellmatta_utils.h"
 #include "shellmatta_utils.h"
@@ -89,7 +89,7 @@ static bool getHistoryByte(shellmatta_instance_t *inst, char *byte)
  * @param[in]   inst    pointer to a shellmatta instance
  * @param[in]   inst    pointer to a shellmatta instance
  * @return      true:   current command is identical to the last one in the history buffer
  * @return      true:   current command is identical to the last one in the history buffer
  */
  */
-static bool compareLastCommand(shellmatta_instance_t *inst)
+static bool compareLastCommand(const shellmatta_instance_t *inst)
 {
 {
     bool        ret = false;
     bool        ret = false;
     uint32_t    i;
     uint32_t    i;
@@ -286,6 +286,19 @@ void history_reset(shellmatta_instance_t *inst)
     inst->historyReadUp = true;
     inst->historyReadUp = true;
 }
 }
 
 
+/**
+ * @brief       clears the history buffer
+ * @param[in]   inst    pointer to a shellmatta instance
+ */
+void history_clear(shellmatta_instance_t *inst)
+{
+    inst->historyStart      = 0u;
+    inst->historyEnd        = 0u;
+    inst->historyRead       = 0u;
+    inst->historyReadUp     = true;
+    memset(inst->historyBuffer, 0, inst->historyBufferSize);
+}
+
 /**
 /**
  * @}
  * @}
  */
  */

+ 2 - 1
src/shellmatta_history.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -26,6 +26,7 @@ bool history_navigate(shellmatta_instance_t *inst, int32_t cnt);
 void history_storeCmd(shellmatta_instance_t *inst);
 void history_storeCmd(shellmatta_instance_t *inst);
 void history_restoreCmd(shellmatta_instance_t *inst);
 void history_restoreCmd(shellmatta_instance_t *inst);
 void history_reset(shellmatta_instance_t *inst);
 void history_reset(shellmatta_instance_t *inst);
+void history_clear(shellmatta_instance_t *inst);
 
 
 #endif
 #endif
 
 

+ 3 - 3
src/shellmatta_opt.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -129,7 +129,7 @@ static shellmatta_retCode_t parseShortOpt(  shellmatta_instance_t       *inst,
                                             shellmatta_opt_argtype_t    *argtype)
                                             shellmatta_opt_argtype_t    *argtype)
 {
 {
     shellmatta_retCode_t ret = SHELLMATTA_ERROR;
     shellmatta_retCode_t ret = SHELLMATTA_ERROR;
-    char *buffer = &inst->buffer[inst->optionParser.offset];
+    const char *buffer = &inst->buffer[inst->optionParser.offset];
     uint32_t i;
     uint32_t i;
 
 
     /** -# check for correct syntax */
     /** -# check for correct syntax */
@@ -191,7 +191,7 @@ static shellmatta_retCode_t parseLongOpt(   shellmatta_instance_t       *inst,
                                             shellmatta_opt_argtype_t    *argtype)
                                             shellmatta_opt_argtype_t    *argtype)
 {
 {
     shellmatta_retCode_t ret = SHELLMATTA_ERROR;
     shellmatta_retCode_t ret = SHELLMATTA_ERROR;
-    char *buffer = &inst->buffer[inst->optionParser.offset];
+    const char *buffer = &inst->buffer[inst->optionParser.offset];
     uint32_t i;
     uint32_t i;
 
 
     /** -# check for correct syntax for short options */
     /** -# check for correct syntax for short options */

+ 1 - 1
src/shellmatta_opt.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this

+ 33 - 3
src/shellmatta_utils.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -19,6 +19,9 @@
 
 
 #include "shellmatta_utils.h"
 #include "shellmatta_utils.h"
 #include "shellmatta.h"
 #include "shellmatta.h"
+#ifdef SHELLMATTA_AUTHENTICATION
+#include "shellmatta_auth.h"
+#endif
 #include <string.h>
 #include <string.h>
 
 
 /**
 /**
@@ -164,7 +167,7 @@ void utils_forwardCursor(shellmatta_instance_t *inst, uint32_t length)
  * @param[in]   length  length of the data to be inserted
  * @param[in]   length  length of the data to be inserted
  */
  */
 void utils_insertChars( shellmatta_instance_t   *inst,
 void utils_insertChars( shellmatta_instance_t   *inst,
-                        char                    *data,
+                        const char              *data,
                         uint32_t                 length)
                         uint32_t                 length)
 {
 {
     uint32_t tmpLength = length;
     uint32_t tmpLength = length;
@@ -189,6 +192,7 @@ void utils_insertChars( shellmatta_instance_t   *inst,
 
 
             /** -# store and print the new chars */
             /** -# store and print the new chars */
             memcpy(&(inst->buffer[inst->cursor]), data, tmpLength);
             memcpy(&(inst->buffer[inst->cursor]), data, tmpLength);
+
             utils_writeEcho(inst, data, tmpLength);
             utils_writeEcho(inst, data, tmpLength);
 
 
             /** -# print the other chars and restore the cursor to this position */
             /** -# print the other chars and restore the cursor to this position */
@@ -385,6 +389,13 @@ static shellmatta_retCode_t helpCmdFct(const shellmatta_handle_t handle, const c
         cmd = inst->cmdList;
         cmd = inst->cmdList;
         while(NULL != cmd)
         while(NULL != cmd)
         {
         {
+#ifdef SHELLMATTA_AUTHENTICATION
+            if (SHELLMATTA_OK != shellmatta_auth_is_cmd_permitted(inst, cmd))
+            {
+                cmd = cmd->next;
+                continue;
+            }
+#endif
             maxCmdLen           = SHELLMATTA_MAX(maxCmdLen,         strlen(cmd->cmd));
             maxCmdLen           = SHELLMATTA_MAX(maxCmdLen,         strlen(cmd->cmd));
             if(NULL != cmd->cmdAlias)
             if(NULL != cmd->cmdAlias)
             {
             {
@@ -397,6 +408,13 @@ static shellmatta_retCode_t helpCmdFct(const shellmatta_handle_t handle, const c
         cmd = inst->cmdList;
         cmd = inst->cmdList;
         while(NULL != cmd)
         while(NULL != cmd)
         {
         {
+#ifdef SHELLMATTA_AUTHENTICATION
+            if (SHELLMATTA_OK != shellmatta_auth_is_cmd_permitted(inst, cmd))
+            {
+                cmd = cmd->next;
+                continue;
+            }
+#endif
             /** -# determine the length of each field to add padding */
             /** -# determine the length of each field to add padding */
             cmdLen       = strlen(cmd->cmd);
             cmdLen       = strlen(cmd->cmd);
             cmdAliasLen  = (NULL != cmd->cmdAlias) ? strlen(cmd->cmdAlias) : 0u;
             cmdAliasLen  = (NULL != cmd->cmdAlias) ? strlen(cmd->cmdAlias) : 0u;
@@ -436,7 +454,11 @@ const shellmatta_cmd_t helpCmd = {SHELLMATTA_HELP_COMMAND
                                 , SHELLMATTA_HELP_HELP_TEXT
                                 , SHELLMATTA_HELP_HELP_TEXT
                                 , SHELLMATTA_HELP_USAGE_TEXT
                                 , SHELLMATTA_HELP_USAGE_TEXT
                                 , helpCmdFct
                                 , helpCmdFct
-                                , NULL};
+                                , NULL
+#ifdef SHELLMATTA_AUTHENTICATION
+                                , NULL
+#endif
+                                };
 
 
 /**
 /**
  * @brief       terminates an input and prints the prompt again
  * @brief       terminates an input and prints the prompt again
@@ -453,6 +475,14 @@ void utils_terminateInput(shellmatta_instance_t *inst)
     inst->continuousCmd     = NULL;
     inst->continuousCmd     = NULL;
     inst->busyCmd           = NULL;
     inst->busyCmd           = NULL;
     SHELLMATTA_WRITE("\r\n", 2u);
     SHELLMATTA_WRITE("\r\n", 2u);
+#ifdef SHELLMATTA_AUTHENTICATION
+    inst->loginState        = SHELLMATTA_AUTH_IDLE;
+    if (NULL != inst->userPointer)
+    {
+        SHELLMATTA_WRITE(inst->userPointer->username, strlen(inst->userPointer->username));
+        SHELLMATTA_WRITE("@", 1);
+    }
+#endif
     SHELLMATTA_WRITE(inst->prompt, strlen(inst->prompt));
     SHELLMATTA_WRITE(inst->prompt, strlen(inst->prompt));
 }
 }
 
 

+ 2 - 2
src/shellmatta_utils.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -116,7 +116,7 @@ void utils_eraseLine(shellmatta_instance_t *inst);
 void utils_rewindCursor(shellmatta_instance_t *inst, uint32_t length);
 void utils_rewindCursor(shellmatta_instance_t *inst, uint32_t length);
 void utils_forwardCursor(shellmatta_instance_t *inst, uint32_t length);
 void utils_forwardCursor(shellmatta_instance_t *inst, uint32_t length);
 void utils_insertChars( shellmatta_instance_t   *inst,
 void utils_insertChars( shellmatta_instance_t   *inst,
-                        char                    *data,
+                        const char              *data,
                         uint32_t                 length);
                         uint32_t                 length);
 void utils_removeChars( shellmatta_instance_t   *inst,
 void utils_removeChars( shellmatta_instance_t   *inst,
                         uint32_t                 length,
                         uint32_t                 length,

File diff suppressed because it is too large
+ 2282 - 1171
test/framework/catch.hpp


+ 31 - 38
test/integrationtest/test_integration.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2021 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -11,12 +11,12 @@
  * @brief   integration test implementation for some general functions
  * @brief   integration test implementation for some general functions
  * @author  Stefan Strobel <stefan.strobel@shimatta.net>
  * @author  Stefan Strobel <stefan.strobel@shimatta.net>
  */
  */
-
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 extern "C" {
 extern "C" {
 #include "shellmatta.h"
 #include "shellmatta.h"
 }
 }
 #include <string.h>
 #include <string.h>
+using Catch::Matchers::Equals;
 
 
 static uint32_t write_callCnt = 0u;
 static uint32_t write_callCnt = 0u;
 static char write_data[1024];
 static char write_data[1024];
@@ -65,7 +65,6 @@ TEST_CASE( "shellmatta empty function" ) {
     shellmatta_handle_t handle;
     shellmatta_handle_t handle;
     char buffer[1024];
     char buffer[1024];
     char historyBuffer[1024];
     char historyBuffer[1024];
-    char *dummyData =   (char*)"\r\nshellmatta->";
 
 
     shellmatta_doInit(  &inst,
     shellmatta_doInit(  &inst,
                         &handle,
                         &handle,
@@ -84,8 +83,7 @@ TEST_CASE( "shellmatta empty function" ) {
     shellmatta_processData(handle, (char*)"\r", 1);
     shellmatta_processData(handle, (char*)"\r", 1);
 
 
     CHECK( write_length == 14u);
     CHECK( write_length == 14u);
-    REQUIRE( strcmp(dummyData, write_data) == 0);
-
+    REQUIRE_THAT( write_data, Equals("\r\nshellmatta->") );
 }
 }
 
 
 TEST_CASE( "shellmatta heredoc test" ) {
 TEST_CASE( "shellmatta heredoc test" ) {
@@ -94,9 +92,6 @@ TEST_CASE( "shellmatta heredoc test" ) {
     shellmatta_handle_t handle;
     shellmatta_handle_t handle;
     char buffer[1024];
     char buffer[1024];
     char historyBuffer[1024];
     char historyBuffer[1024];
-    char *dummyData =   (char*)"do this ";
-    char *dummyStdin =  (char*)"asdf\r\n"
-                        "1234";
 
 
     shellmatta_doInit(  &inst,
     shellmatta_doInit(  &inst,
                         &handle,
                         &handle,
@@ -119,9 +114,9 @@ TEST_CASE( "shellmatta heredoc test" ) {
                                 , 33);
                                 , 33);
 
 
     CHECK( doSomethingStdinLength == 10u);
     CHECK( doSomethingStdinLength == 10u);
-    CHECK( strcmp(dummyStdin, doSomethingStdin) == 0);
+    CHECK_THAT( doSomethingStdin, Equals("asdf\r\n1234") );
     CHECK( doSomethingLength == 8u);
     CHECK( doSomethingLength == 8u);
-    REQUIRE( strcmp(dummyData, doSomethingArguments) == 0);
+    REQUIRE_THAT( doSomethingArguments, Equals("do this ") );
 }
 }
 
 
 TEST_CASE( "shellmatta heredoc test empty" ) {
 TEST_CASE( "shellmatta heredoc test empty" ) {
@@ -130,7 +125,6 @@ TEST_CASE( "shellmatta heredoc test empty" ) {
     shellmatta_handle_t handle;
     shellmatta_handle_t handle;
     char buffer[1024];
     char buffer[1024];
     char historyBuffer[1024];
     char historyBuffer[1024];
-    char *dummyData =   (char*)"do this ";
 
 
     shellmatta_doInit(  &inst,
     shellmatta_doInit(  &inst,
                         &handle,
                         &handle,
@@ -153,7 +147,7 @@ TEST_CASE( "shellmatta heredoc test empty" ) {
     CHECK( doSomethingStdinLength == 0u);
     CHECK( doSomethingStdinLength == 0u);
     CHECK( NULL == doSomethingStdin );
     CHECK( NULL == doSomethingStdin );
     CHECK( doSomethingLength == 8u);
     CHECK( doSomethingLength == 8u);
-    REQUIRE( strcmp(dummyData, doSomethingArguments) == 0);
+    REQUIRE_THAT( doSomethingArguments, Equals("do this ") );
 }
 }
 
 
 TEST_CASE( "shellmatta remove function" ) {
 TEST_CASE( "shellmatta remove function" ) {
@@ -185,8 +179,7 @@ TEST_CASE( "shellmatta remove function" ) {
     shellmatta_processData(handle, (char*)"?\r", 2);
     shellmatta_processData(handle, (char*)"?\r", 2);
 
 
     CHECK( write_length == strlen(dummyData));
     CHECK( write_length == strlen(dummyData));
-    CHECK( strcmp(dummyData, write_data) == 0);
-
+    CHECK_THAT( write_data, Equals(dummyData) );
 
 
     write_callCnt = 0u;
     write_callCnt = 0u;
     memset(write_data, 0, sizeof(write_data));
     memset(write_data, 0, sizeof(write_data));
@@ -199,8 +192,8 @@ TEST_CASE( "shellmatta remove function" ) {
 
 
     shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);
     shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);
 
 
-    CHECK( write_length == strlen(dummyData));
-    CHECK( strcmp(dummyData, write_data) == 0);
+    CHECK(write_length == strlen(dummyData));
+    CHECK_THAT(write_data, Catch::Matchers::Equals(dummyData));
 
 
     write_callCnt = 0u;
     write_callCnt = 0u;
     memset(write_data, 0, sizeof(write_data));
     memset(write_data, 0, sizeof(write_data));
@@ -213,8 +206,8 @@ TEST_CASE( "shellmatta remove function" ) {
                     "help  ?  help [command] - print help or usage information\r\n"
                     "help  ?  help [command] - print help or usage information\r\n"
                     "\r\nshellmatta->";
                     "\r\nshellmatta->";
 
 
-    CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    CHECK(write_length == strlen(dummyData));
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }
 
 
 TEST_CASE( "shellmatta reset no prompt" ) {
 TEST_CASE( "shellmatta reset no prompt" ) {
@@ -247,8 +240,8 @@ TEST_CASE( "shellmatta reset no prompt" ) {
     shellmatta_resetShell(handle, false);
     shellmatta_resetShell(handle, false);
     shellmatta_processData(handle, (char*)"?\r", 2);
     shellmatta_processData(handle, (char*)"?\r", 2);
 
 
-    CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    CHECK(write_length == strlen(dummyData));
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }
 
 
 TEST_CASE( "shellmatta reset with prompt" ) {
 TEST_CASE( "shellmatta reset with prompt" ) {
@@ -282,8 +275,8 @@ TEST_CASE( "shellmatta reset with prompt" ) {
     shellmatta_resetShell(handle, true);
     shellmatta_resetShell(handle, true);
     shellmatta_processData(handle, (char*)"?\r", 2);
     shellmatta_processData(handle, (char*)"?\r", 2);
 
 
-    CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    CHECK(write_length == strlen(dummyData));
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }
 
 
 TEST_CASE( "shellmatta reset no prompt history buffer" ) {
 TEST_CASE( "shellmatta reset no prompt history buffer" ) {
@@ -322,8 +315,8 @@ TEST_CASE( "shellmatta reset no prompt history buffer" ) {
     shellmatta_processData(handle, (char*)"\033[A", 3u);
     shellmatta_processData(handle, (char*)"\033[A", 3u);
     shellmatta_processData(handle, (char*)"?\r", 2);
     shellmatta_processData(handle, (char*)"?\r", 2);
 
 
-    CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    CHECK(write_length == strlen(dummyData));
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }
 
 
 TEST_CASE( "shellmatta reset no prompt heredoc" ) {
 TEST_CASE( "shellmatta reset no prompt heredoc" ) {
@@ -362,8 +355,8 @@ TEST_CASE( "shellmatta reset no prompt heredoc" ) {
     /* now the new command should be processed */
     /* now the new command should be processed */
     shellmatta_processData(handle, (char*)"?\r", 2);
     shellmatta_processData(handle, (char*)"?\r", 2);
 
 
-    CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    CHECK(write_length == strlen(dummyData));
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }
 
 
 TEST_CASE( "shellmatta configure disable echo" ) {
 TEST_CASE( "shellmatta configure disable echo" ) {
@@ -400,8 +393,8 @@ TEST_CASE( "shellmatta configure disable echo" ) {
     /* check with echo enabled */
     /* check with echo enabled */
     shellmatta_processData(handle, (char*)"help this is some dummy Text\r\n", 30u);
     shellmatta_processData(handle, (char*)"help this is some dummy Text\r\n", 30u);
 
 
-    CHECK( write_length == strlen(dummyData));
-    CHECK( strcmp(dummyData, write_data) == 0);
+    CHECK(write_length == strlen(dummyData));
+    CHECK_THAT(write_data, Catch::Matchers::Equals(dummyData));
 
 
     write_callCnt = 0u;
     write_callCnt = 0u;
     memset(write_data, 0, sizeof(write_data));
     memset(write_data, 0, sizeof(write_data));
@@ -414,8 +407,8 @@ TEST_CASE( "shellmatta configure disable echo" ) {
     /* check with echo disabled */
     /* check with echo disabled */
     shellmatta_processData(handle, (char*)"help this is some dummy Text\r\n", 30u);
     shellmatta_processData(handle, (char*)"help this is some dummy Text\r\n", 30u);
 
 
-    CHECK( write_length == strlen(dummyData2));
-    REQUIRE( strcmp(dummyData2, write_data) == 0);
+    CHECK(write_length == strlen(dummyData2));
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData2));
 }
 }
 
 
 TEST_CASE( "shellmatta configure mode" ) {
 TEST_CASE( "shellmatta configure mode" ) {
@@ -425,9 +418,9 @@ TEST_CASE( "shellmatta configure mode" ) {
     shellmatta_retCode_t ret;
     shellmatta_retCode_t ret;
     char buffer[1024];
     char buffer[1024];
     char historyBuffer[1024];
     char historyBuffer[1024];
-    char *dummyData =   (char*)"\r\nCommand: meow this is some dum123456my Text not found\r\nshellmatta->";
+    char *dummyData =   (char*)"\r\nCommand not found!\r\nshellmatta->";
 
 
-    char *dummyData2 =   (char*)"\r\nCommand: meow this is some dum123456t not found\r\nshellmatta->";
+    char *dummyData2 =   (char*)"\r\nCommand not found!\r\nshellmatta->";
 
 
     shellmatta_doInit(  &inst,
     shellmatta_doInit(  &inst,
                         &handle,
                         &handle,
@@ -448,8 +441,8 @@ TEST_CASE( "shellmatta configure mode" ) {
     /* check with insert mode */
     /* check with insert mode */
     shellmatta_processData(handle, (char*)"meow this is some dummy Text\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D123456\r\n", 57u);
     shellmatta_processData(handle, (char*)"meow this is some dummy Text\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D123456\r\n", 57u);
 
 
-    CHECK( write_length == strlen(dummyData));
-    CHECK( strcmp(dummyData, write_data) == 0);
+    CHECK(write_length == strlen(dummyData));
+    CHECK_THAT(write_data, Catch::Matchers::Equals(dummyData));
 
 
     write_callCnt = 0u;
     write_callCnt = 0u;
     memset(write_data, 0, sizeof(write_data));
     memset(write_data, 0, sizeof(write_data));
@@ -462,8 +455,8 @@ TEST_CASE( "shellmatta configure mode" ) {
     /* check with echo disabled */
     /* check with echo disabled */
     shellmatta_processData(handle, (char*)"meow this is some dummy Text\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D123456\r\n", 57u);
     shellmatta_processData(handle, (char*)"meow this is some dummy Text\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D123456\r\n", 57u);
 
 
-    CHECK( write_length == strlen(dummyData2));
-    REQUIRE( strcmp(dummyData2, write_data) == 0);
+    CHECK(write_length == strlen(dummyData2));
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData2));
 }
 }
 
 
 TEST_CASE( "shellmatta configure delimiter" ) {
 TEST_CASE( "shellmatta configure delimiter" ) {
@@ -508,6 +501,6 @@ TEST_CASE( "shellmatta configure delimiter" ) {
     /* check with echo disabled */
     /* check with echo disabled */
     shellmatta_processData(handle, (char*)"doSomething argument\n", 21u);
     shellmatta_processData(handle, (char*)"doSomething argument\n", 21u);
 
 
-    CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    CHECK(write_length == strlen(dummyData));
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }

+ 3 - 3
test/integrationtest/test_integration_busy.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -133,7 +133,7 @@ TEST_CASE( "shellmatta busy 1" ) {
     CHECK( 10u  == busyCallCnt);
     CHECK( 10u  == busyCallCnt);
     CHECK( 1u   == notBusyCallCnt );
     CHECK( 1u   == notBusyCallCnt );
     CHECK( write_length == strlen(dummyData));
     CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }
 
 
 TEST_CASE( "shellmatta busy suspend with continuous mode" ) {
 TEST_CASE( "shellmatta busy suspend with continuous mode" ) {
@@ -179,5 +179,5 @@ TEST_CASE( "shellmatta busy suspend with continuous mode" ) {
     CHECK( 6u  == busyCallCnt);
     CHECK( 6u  == busyCallCnt);
     CHECK( 0u   == notBusyCallCnt );
     CHECK( 0u   == notBusyCallCnt );
     CHECK( write_length == strlen(dummyData));
     CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }

+ 5 - 5
test/integrationtest/test_integration_continue.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -74,7 +74,7 @@ static shellmatta_retCode_t continueCmdFct(shellmatta_handle_t handle, const cha
 
 
     /* -# the arguments shall stay the same on every call - data is transferred per stdin */
     /* -# the arguments shall stay the same on every call - data is transferred per stdin */
     CHECK(length == 28u);
     CHECK(length == 28u);
-    CHECK(strcmp(arguments, "continue some arguments meow") == 0);
+    CHECK_THAT(arguments, Catch::Matchers::Equals("continue some arguments meow"));
 
 
     contCallCnt ++;
     contCallCnt ++;
 
 
@@ -148,7 +148,7 @@ TEST_CASE( "shellmatta continue 1" ) {
     CHECK( 0u  == busyCallCnt);
     CHECK( 0u  == busyCallCnt);
     CHECK( 10u  == contCallCnt);
     CHECK( 10u  == contCallCnt);
     CHECK( write_length == strlen(dummyData));
     CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }
 
 
 TEST_CASE( "shellmatta continue cancel" ) {
 TEST_CASE( "shellmatta continue cancel" ) {
@@ -207,7 +207,7 @@ TEST_CASE( "shellmatta continue cancel" ) {
     CHECK( 0u  == busyCallCnt);
     CHECK( 0u  == busyCallCnt);
     CHECK( 5u  == contCallCnt);
     CHECK( 5u  == contCallCnt);
     CHECK( write_length == strlen(dummyData));
     CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }
 
 
 TEST_CASE( "shellmatta continue suspend with busy mode" ) {
 TEST_CASE( "shellmatta continue suspend with busy mode" ) {
@@ -280,5 +280,5 @@ TEST_CASE( "shellmatta continue suspend with busy mode" ) {
     CHECK( 10u  == contCallCnt);
     CHECK( 10u  == contCallCnt);
     CHECK( 2u  == busyCallCnt);
     CHECK( 2u  == busyCallCnt);
     CHECK( write_length == strlen(dummyData));
     CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }

+ 9 - 9
test/integrationtest/test_integration_help.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2021 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -97,7 +97,7 @@ SCENARIO("Test the help function")
                                                         "shellmatta->";
                                                         "shellmatta->";
                 CHECK(writeFct_fake.call_count == 23);
                 CHECK(writeFct_fake.call_count == 23);
                 CHECK(strlen(response) == fakeWriteLength);
                 CHECK(strlen(response) == fakeWriteLength);
-                CHECK(0 == strcmp(response, fakeWriteData));
+                CHECK_THAT(response, Catch::Matchers::Equals(fakeWriteData));
             }
             }
         }
         }
 
 
@@ -122,7 +122,7 @@ SCENARIO("Test the help function")
                                                         "shellmatta->";
                                                         "shellmatta->";
                 CHECK(writeFct_fake.call_count == 20);
                 CHECK(writeFct_fake.call_count == 20);
                 CHECK(strlen(response) == fakeWriteLength);
                 CHECK(strlen(response) == fakeWriteLength);
-                CHECK(0 == strcmp(response, fakeWriteData));
+                CHECK_THAT(response, Catch::Matchers::Equals(fakeWriteData));
             }
             }
         }
         }
     }
     }
@@ -172,7 +172,7 @@ SCENARIO("Test if the help command prints the usage correctly")
                                                         "\r\nshellmatta->";
                                                         "\r\nshellmatta->";
                 CHECK(writeFct_fake.call_count == 22);
                 CHECK(writeFct_fake.call_count == 22);
                 CHECK(strlen(response) == fakeWriteLength);
                 CHECK(strlen(response) == fakeWriteLength);
-                CHECK(0 == strcmp(response, fakeWriteData));
+                CHECK_THAT(response, Catch::Matchers::Equals(fakeWriteData));
             }
             }
         }
         }
 
 
@@ -197,7 +197,7 @@ SCENARIO("Test if the help command prints the usage correctly")
                                                         "\r\nshellmatta->";
                                                         "\r\nshellmatta->";
                 CHECK(writeFct_fake.call_count == 19);
                 CHECK(writeFct_fake.call_count == 19);
                 CHECK(strlen(response) == fakeWriteLength);
                 CHECK(strlen(response) == fakeWriteLength);
-                CHECK(0 == strcmp(response, fakeWriteData));
+                CHECK_THAT(response, Catch::Matchers::Equals(fakeWriteData));
             }
             }
         }
         }
 
 
@@ -219,7 +219,7 @@ SCENARIO("Test if the help command prints the usage correctly")
                                                         "\r\nshellmatta->";
                                                         "\r\nshellmatta->";
                 CHECK(writeFct_fake.call_count == 15);
                 CHECK(writeFct_fake.call_count == 15);
                 CHECK(strlen(response) == fakeWriteLength);
                 CHECK(strlen(response) == fakeWriteLength);
-                CHECK(0 == strcmp(response, fakeWriteData));
+                CHECK_THAT(response, Catch::Matchers::Equals(fakeWriteData));
             }
             }
         }
         }
 
 
@@ -242,7 +242,7 @@ SCENARIO("Test if the help command prints the usage correctly")
                                                         "\r\nshellmatta->";
                                                         "\r\nshellmatta->";
                 CHECK(writeFct_fake.call_count == 15);
                 CHECK(writeFct_fake.call_count == 15);
                 CHECK(strlen(response) == fakeWriteLength);
                 CHECK(strlen(response) == fakeWriteLength);
-                CHECK(0 == strcmp(response, fakeWriteData));
+                CHECK_THAT(response, Catch::Matchers::Equals(fakeWriteData));
             }
             }
         }
         }
 
 
@@ -264,7 +264,7 @@ SCENARIO("Test if the help command prints the usage correctly")
                                                         "\r\nshellmatta->";
                                                         "\r\nshellmatta->";
                 CHECK(writeFct_fake.call_count == 45);
                 CHECK(writeFct_fake.call_count == 45);
                 CHECK(strlen(response) == fakeWriteLength);
                 CHECK(strlen(response) == fakeWriteLength);
-                CHECK(0 == strcmp(response, fakeWriteData));
+                CHECK_THAT(response, Catch::Matchers::Equals(fakeWriteData));
             }
             }
         }
         }
 
 
@@ -289,7 +289,7 @@ SCENARIO("Test if the help command prints the usage correctly")
                                                         "shellmatta->";
                                                         "shellmatta->";
                 CHECK(writeFct_fake.call_count == 28);
                 CHECK(writeFct_fake.call_count == 28);
                 CHECK(strlen(response) == fakeWriteLength);
                 CHECK(strlen(response) == fakeWriteLength);
-                CHECK(0 == strcmp(response, fakeWriteData));
+                CHECK_THAT(response, Catch::Matchers::Equals(fakeWriteData));
             }
             }
         }
         }
     }
     }

+ 1 - 1
test/integrationtest/test_integration_history.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2021 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this

+ 3 - 3
test/integrationtest/test_integration_opt.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -175,7 +175,7 @@ TEST_CASE( "shellmatta option parser 1" ) {
     CHECK( lenE == 4u );
     CHECK( lenE == 4u );
     CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
     CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
     CHECK( write_length == strlen(dummyData));
     CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }
 
 
 TEST_CASE( "shellmatta option parser 2 - ignore \"--\"" ) {
 TEST_CASE( "shellmatta option parser 2 - ignore \"--\"" ) {
@@ -213,6 +213,6 @@ TEST_CASE( "shellmatta option parser 2 - ignore \"--\"" ) {
     CHECK( lenE == 4u );
     CHECK( lenE == 4u );
     CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
     CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
     CHECK( write_length == strlen(dummyData));
     CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }
 
 

+ 3 - 3
test/integrationtest/test_integration_optLong.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -185,7 +185,7 @@ TEST_CASE( "shellmatta long option parser 1" ) {
     CHECK( lenE == 4u );
     CHECK( lenE == 4u );
     CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
     CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
     CHECK( write_length == strlen(dummyData));
     CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }
 
 
 TEST_CASE( "shellmatta long option parser 2" ) {
 TEST_CASE( "shellmatta long option parser 2" ) {
@@ -225,5 +225,5 @@ TEST_CASE( "shellmatta long option parser 2" ) {
     CHECK( cntDef == 2u );
     CHECK( cntDef == 2u );
     CHECK( lenDef == 2u );
     CHECK( lenDef == 2u );
     CHECK( write_length == strlen(dummyData));
     CHECK( write_length == strlen(dummyData));
-    REQUIRE( strcmp(dummyData, write_data) == 0);
+    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
 }
 }

+ 847 - 0
test/integrationtest_auth/test_integration_auth.cpp

@@ -0,0 +1,847 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_integration_auth.cpp
+ * @brief   integration test implementation for the authentication functions
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#include "test/framework/catch.hpp"
+extern "C" {
+#include "shellmatta.h"
+}
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static char write_data[1024];
+static uint32_t write_length;
+static uint32_t privateCallCount;
+static uint32_t logUserId;
+static shellmatta_auth_log_event_t logEvent;
+
+#define TEST_SHELLMATTA_SETUP   shellmatta_retCode_t ret;                   \
+                                shellmatta_instance_t inst;                 \
+                                shellmatta_handle_t handle;                 \
+                                char buffer[1024] = {0};                    \
+                                char historyBuffer[1024] = {0};             \
+                                                                            \
+                                shellmatta_doInit(  &inst,                  \
+                                                    &handle,                \
+                                                    buffer,                 \
+                                                    sizeof(buffer),         \
+                                                    historyBuffer,          \
+                                                    sizeof(historyBuffer),  \
+                                                    "shellmatta->",         \
+                                                    NULL,                   \
+                                                    writeFct);              \
+                                                                            \
+                                write_callCnt = 0u;                         \
+                                memset(write_data, 0, sizeof(write_data));  \
+                                write_length = 0u;                          \
+                                privateCallCount = 0u;                      \
+                                logUserId = 255u;                           \
+                                logEvent = SHELLMATTA_AUTH_EVENT_NONE;      \
+                                                                            \
+                                shellmatta_addCmd(handle, &publicCmd);      \
+                                shellmatta_addCmd(handle, &privateCmd);
+
+#define TEST_SHELLMATTA_AUTH_SETUP  shellmatta_auth_user_t userList[] = {                                                   \
+                                        {1, false, "shimatta", "12345678"},                                                 \
+                                        {2, false, "not_shimatta", "87654321"},                                             \
+                                        {3, true, "root", "rootpw"}                                                         \
+                                    };                                                                                      \
+                                                                                                                            \
+                                    uint32_t privateCmdPerms[] = {1};                                                       \
+                                    shellmatta_auth_perm_t permList[] = {                                                   \
+                                        {"private", privateCmdPerms, sizeof(privateCmdPerms)/sizeof(privateCmdPerms[0])},   \
+                                        {"additional", privateCmdPerms, sizeof(privateCmdPerms)/sizeof(privateCmdPerms[0])} \
+                                    };                                                                                      \
+                                                                                                                            \
+                                    shellmatta_auth_init(handle, userList, 3, permList, 2, false, NULL, NULL);
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    while((length > 0) && (write_length < sizeof(write_data)))
+    {
+        write_data[write_length] = *data;
+        data ++;
+        length --;
+        write_length ++;
+    }
+
+    return SHELLMATTA_OK;
+}
+
+static shellmatta_retCode_t publicCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)      handle;
+    (void)      arguments;
+    (void)      length;
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+
+    return ret;
+}
+shellmatta_cmd_t publicCmd = {(char*)"public", (char*)"p", NULL, NULL, publicCmdFct, NULL, NULL};
+
+
+static shellmatta_retCode_t privateCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)      handle;
+    (void)      arguments;
+    (void)      length;
+
+    privateCallCount ++;
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t privateCmd = {(char*)"private", (char*)"r", NULL, NULL, privateCmdFct, NULL, NULL};
+
+
+static shellmatta_retCode_t additionalCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)      handle;
+    (void)      arguments;
+    (void)      length;
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t additionalCmd = {(char*)"additional", (char*)"a", NULL, NULL, additionalCmdFct, NULL, NULL};
+
+
+SCENARIO("Check help auth uninitialized") {
+    GIVEN("An initialized shellmatta instance without initialized auth") {
+
+        TEST_SHELLMATTA_SETUP;
+
+        WHEN("The help command is called") {
+
+            ret = shellmatta_processData(handle, (char*)"help\r", 5);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("The help command prints all commands - without login/logout.") {
+
+                char *dummyData =   (char*) "help\r\n"
+                                            "help     ?  help [command] - print help or usage information\r\n"
+                                            "private  r\r\n"
+                                            "public   p\r\n"
+                                            "\r\n"
+                                            "shellmatta->";
+
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+            }
+        }
+    }
+}
+
+SCENARIO("Check auth unauthorized") {
+    GIVEN("An initialized shellmatta instance with initialized auth") {
+
+        TEST_SHELLMATTA_SETUP;
+
+        TEST_SHELLMATTA_AUTH_SETUP;
+
+        WHEN("The help command is called") {
+
+            ret = shellmatta_processData(handle, (char*)"help\r", 5);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("The help command prints only public commands.") {
+
+                char *dummyData =   (char*) "help\r\n"
+                                            "help    ?   help [command] - print help or usage information\r\n"
+                                            "login   li  Login command\r\n"
+                                            "logout  lo  Logout command\r\n"
+                                            "public  p\r\n"
+                                            "\r\n"
+                                            "shellmatta->";
+
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+            }
+        }
+
+        WHEN("A private function is called") {
+
+            ret = shellmatta_processData(handle, (char*)"private\r", 8);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("The command is not executed.") {
+
+                char *dummyData =   (char*) "private\r\n"
+                                            "\r\n"
+                                            "Command: private not found\r\n"
+                                            "shellmatta->";
+
+                CHECK(privateCallCount == 0u);
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+            }
+        }
+
+        WHEN("Autocomplete is triggered") {
+
+            ret = shellmatta_processData(handle, (char*)"\t\t", 2);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("Only public commands are shown.") {
+
+                char *dummyData =   (char*) "\r\n"
+                                            "help    ?    login    li    logout    lo    public    p    \r\n"
+                                            "shellmatta->";
+
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+            }
+        }
+    }
+}
+
+SCENARIO("Check authorized") {
+    GIVEN("An initialized shellmatta instance with initialized auth") {
+
+        TEST_SHELLMATTA_SETUP;
+
+        TEST_SHELLMATTA_AUTH_SETUP;
+
+        WHEN("The user shimatta is logged in") {
+
+            ret = shellmatta_auth_login(handle, 1);
+            CHECK(ret == SHELLMATTA_OK);
+
+            AND_WHEN("The help command is called") {
+
+                ret = shellmatta_processData(handle, (char*)"help\r", 5);
+                CHECK(ret == SHELLMATTA_OK);
+
+                THEN("The help command prints all commands.") {
+
+                    char *dummyData =   (char*) "help\r\n"
+                                                "help     ?   help [command] - print help or usage information\r\n"
+                                                "login    li  Login command\r\n"
+                                                "logout   lo  Logout command\r\n"
+                                                "private  r\r\n"
+                                                "public   p\r\n"
+                                                "\r\n"
+                                                "shimatta@shellmatta->";
+
+                    CHECK(write_length == strlen(dummyData));
+                    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                    AND_THEN("The shimatta user is logged in") {
+                        char usernameBuffer[16] = {0};
+                        uint32_t usernameBufferLength = sizeof(usernameBuffer);
+
+                        CHECK(1 == shellmatta_auth_getLoggedInUserId(handle));
+                        ret = shellmatta_auth_getLoggedInUserName(handle, usernameBuffer, &usernameBufferLength);
+                        CHECK(ret == SHELLMATTA_OK);
+                        CHECK(usernameBufferLength == strlen("shimatta"));
+                        REQUIRE_THAT(usernameBuffer, Catch::Matchers::Equals("shimatta"));
+                    }
+                }
+
+                AND_WHEN("The user is logged out using the logout command") {
+                    write_length = 0;
+                    memset(write_data, 0, sizeof(write_data));
+                    ret = shellmatta_processData(handle, (char*)"logout\r", 7);
+                    CHECK(ret == SHELLMATTA_OK);
+
+                    THEN("The Shellmatta prints the logout message") {
+                        char *dummyData =   (char*) "logout\r\n"
+                                                    "good bye\r\n\r\n"
+                                                    "shellmatta->";
+                        CHECK(write_length == strlen(dummyData));
+                        REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+                    }
+
+                    AND_WHEN("The help command is called") {
+
+                        write_length = 0;
+                        memset(write_data, 0, sizeof(write_data));
+                        ret = shellmatta_processData(handle, (char*)"help\r", 5);
+                        CHECK(ret == SHELLMATTA_OK);
+
+                        THEN("The help command prints only public commands.") {
+
+                            char *dummyData =   (char*) "help\r\n"
+                                                        "help    ?   help [command] - print help or usage information\r\n"
+                                                        "login   li  Login command\r\n"
+                                                        "logout  lo  Logout command\r\n"
+                                                        "public  p\r\n"
+                                                        "\r\n"
+                                                        "shellmatta->";
+
+                            CHECK(write_length == strlen(dummyData));
+                            REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+                        }
+                    }
+                }
+            }
+            AND_WHEN("A private function is called") {
+
+                ret = shellmatta_processData(handle, (char*)"private\r", 8);
+                CHECK(ret == SHELLMATTA_OK);
+
+                THEN("The command executed.") {
+
+                    char *dummyData =   (char*) "private\r\n"
+                                                "\r\n"
+                                                "shimatta@shellmatta->";
+
+                    CHECK(privateCallCount == 1u);
+                    CHECK(write_length == strlen(dummyData));
+                    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+                }
+            }
+        }
+        WHEN("The user not_shellmatta is logged in") {
+
+            ret = shellmatta_auth_login(handle, 2);
+            CHECK(ret == SHELLMATTA_OK);
+
+            AND_WHEN("The help command is called") {
+
+                ret = shellmatta_processData(handle, (char*)"help\r", 5);
+                CHECK(ret == SHELLMATTA_OK);
+
+                THEN("The help command prints not all commands.") {
+
+                    char *dummyData =   (char*) "help\r\n"
+                                                "help    ?   help [command] - print help or usage information\r\n"
+                                                "login   li  Login command\r\n"
+                                                "logout  lo  Logout command\r\n"
+                                                "public  p\r\n"
+                                                "\r\n"
+                                                "not_shimatta@shellmatta->";
+
+                    CHECK(ret == SHELLMATTA_OK);
+                    CHECK(write_length == strlen(dummyData));
+                    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                    AND_THEN("The not_shimatta user is logged in") {
+                        char usernameBuffer[16] = {0};
+                        uint32_t usernameBufferLength = sizeof(usernameBuffer);
+
+                        CHECK(2 == shellmatta_auth_getLoggedInUserId(handle));
+                        ret = shellmatta_auth_getLoggedInUserName(handle, usernameBuffer, &usernameBufferLength);
+                        CHECK(ret == SHELLMATTA_OK);
+                        CHECK(usernameBufferLength == strlen("not_shimatta"));
+                        REQUIRE_THAT(usernameBuffer, Catch::Matchers::Equals("not_shimatta"));
+                    }
+                }
+            }
+            AND_WHEN("A private function is called") {
+
+                ret = shellmatta_processData(handle, (char*)"private\r", 8);
+                CHECK(ret == SHELLMATTA_OK);
+
+                THEN("The command is not executed.") {
+
+                    char *dummyData =   (char*) "private\r\n"
+                                                "\r\n"
+                                                "Command: private not found\r\n"
+                                                "not_shimatta@shellmatta->";
+
+                    CHECK(privateCallCount == 0u);
+                    CHECK(write_length == strlen(dummyData));
+                    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+                }
+            }
+        }
+        WHEN("The user root is logged in") {
+            ret = shellmatta_auth_login(handle, 3);
+            CHECK(ret == SHELLMATTA_OK);
+
+            AND_WHEN("The help command is called") {
+
+                ret = shellmatta_processData(handle, (char*)"help\r", 5);
+                CHECK(ret == SHELLMATTA_OK);
+
+                THEN("The help command prints all commands.") {
+
+                    char *dummyData =   (char*) "help\r\n"
+                                                "help     ?   help [command] - print help or usage information\r\n"
+                                                "login    li  Login command\r\n"
+                                                "logout   lo  Logout command\r\n"
+                                                "private  r\r\n"
+                                                "public   p\r\n"
+                                                "\r\n"
+                                                "root@shellmatta->";
+
+                    CHECK(ret == SHELLMATTA_OK);
+                    CHECK(write_length == strlen(dummyData));
+                    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+                }
+            }
+        }
+    }
+}
+
+SCENARIO("Check login command with privileged user") {
+    GIVEN("An initialized shellmatta instance with initialized auth") {
+
+        TEST_SHELLMATTA_SETUP;
+
+        TEST_SHELLMATTA_AUTH_SETUP;
+
+        WHEN("The user shimatta logs in interactively using the correct credentials") {
+
+            ret = shellmatta_processData(handle, (char*)"login\r\n"
+                                                        "shimatta\r\n"
+                                                        "12345678\r", 26);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("The login message is printed - password is hidden") {
+                char *dummyData =   (char*) "login\r\n"
+                                            "enter username:\r\n"
+                                            "shimatta\r\n"
+                                            "enter password:\r\n"
+                                            "\r\n"
+                                            "login successful\r\n"
+                                            "\r\n"
+                                            "shimatta@shellmatta->";
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                AND_THEN("The shimatta user is logged in") {
+                    REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+            }
+        }
+
+        WHEN("The user shimatta logs in interactively using wrong credentials") {
+
+            ret = shellmatta_processData(handle, (char*)"login\r\n"
+                                                        "shimatta\r\n"
+                                                        "11111111\r", 26);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("A warning message is printed") {
+                char *dummyData =   (char*) "login\r\n"
+                                            "enter username:\r\n"
+                                            "shimatta\r\n"
+                                            "enter password:\r\n"
+                                            "\r\n"
+                                            "username or password is wrong\r\n"
+                                            "\r\n"
+                                            "shellmatta->";
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                AND_THEN("The shimatta user is not logged in") {
+                    REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+            }
+        }
+
+        WHEN("The user shimatta logs in interactively manipulating the input") {
+
+            ret = shellmatta_processData(handle, (char*)"login\r"
+                                                        "shimg\batta\r"
+                                                        "1h" "\x7f" "2346\033[D5\033[C78\b8\r", 36);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("The login message is printed - password is hidden") {
+                char *dummyData =   (char*) "login\r\n"
+                                            "enter username:\r\n"
+                                            "shimg\033[1D\033[K\033[s\033[uatta\r\n"
+                                            "enter password:\r\n"
+                                            "\r\n"
+                                            "login successful\r\n"
+                                            "\r\n"
+                                            "shimatta@shellmatta->";
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                AND_THEN("The shimatta user is logged in") {
+                    REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+            }
+        }
+
+        WHEN("The user shimatta logs in passing the credentials none interactively") {
+
+            ret = shellmatta_processData(handle, (char*)"login -u shimatta -p 12345678\r", 30);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("The login message is printed") {
+                char *dummyData =   (char*) "login -u shimatta -p 12345678\r\n"
+                                            "login successful\r\n"
+                                            "\r\n"
+                                            "shimatta@shellmatta->";
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                AND_THEN("The shimatta user is logged in") {
+                    REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+            }
+        }
+
+        WHEN("The user shimatta logs in passing the credentials half interactively") {
+
+            ret = shellmatta_processData(handle, (char*)"login -u shimatta\r12345678\r", 27);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("The login message is printed - password is hidden") {
+                char *dummyData =   (char*) "login -u shimatta\r\n\r\n"
+                                            "enter password:\r\n"
+                                            "\r\n"
+                                            "login successful\r\n"
+                                            "\r\n"
+                                            "shimatta@shellmatta->";
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                AND_THEN("The shimatta user is logged in") {
+                    REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+            }
+        }
+
+        WHEN("The user shimatta tries to login non interactively without username") {
+
+            ret = shellmatta_processData(handle, (char*)"login -p 12345678\r", 18);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("A warning message is printed") {
+                char *dummyData =   (char*) "login -p 12345678\r\n"
+                                            "Missing username\r\n"
+                                            "\r\n"
+                                            "shellmatta->";
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                AND_THEN("The shimatta user is not logged in") {
+                    REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+            }
+        }
+
+        WHEN("The user shimatta tries to login using the wrong options") {
+
+            ret = shellmatta_processData(handle, (char*)"login -o meow\r", 14);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("A warning message is printed") {
+                char *dummyData =   (char*) "login -o meow\r\n"
+                                            "Unknown option\r\n"
+                                            "\r\n"
+                                            "shellmatta->";
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                AND_THEN("The shimatta user is not logged in") {
+                    REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+            }
+        }
+    }
+}
+
+SCENARIO("Check login command with unprivileged user") {
+    GIVEN("An initialized shellmatta instance with initialized auth") {
+
+        TEST_SHELLMATTA_SETUP;
+
+        TEST_SHELLMATTA_AUTH_SETUP;
+
+        WHEN("The user not_shimatta logs in interactively using the correct credentials") {
+
+            ret = shellmatta_processData(handle, (char*)"login\r"
+                                                        "not_shimatta\r"
+                                                        "87654321\r", 28);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("The login message is printed - password is hidden") {
+                char *dummyData =   (char*) "login\r\n"
+                                            "enter username:\r\n"
+                                            "not_shimatta\r\n"
+                                            "enter password:\r\n"
+                                            "\r\n"
+                                            "login successful\r\n"
+                                            "\r\n"
+                                            "not_shimatta@shellmatta->";
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                AND_THEN("The not_shimatta user is logged in") {
+                    REQUIRE(2 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+            }
+        }
+    }
+}
+
+SCENARIO("Check adding commands after the authentication is initialized") {
+    GIVEN("An initialized shellmatta instance with initialized auth") {
+
+        TEST_SHELLMATTA_SETUP;
+
+        TEST_SHELLMATTA_AUTH_SETUP;
+
+        WHEN("A command is added") {
+
+            ret = shellmatta_addCmd(handle, &additionalCmd);
+            CHECK(ret == SHELLMATTA_OK);
+
+            AND_WHEN("The help command is called") {
+
+                ret = shellmatta_processData(handle, (char*)"help\r", 5);
+                CHECK(ret == SHELLMATTA_OK);
+
+                THEN("The additional command is not shown as it requires login") {
+                    char *dummyData =   (char*) "help\r\n"
+                                                "help    ?   help [command] - print help or usage information\r\n"
+                                                "login   li  Login command\r\n"
+                                                "logout  lo  Logout command\r\n"
+                                                "public  p\r\n"
+                                                "\r\n"
+                                                "shellmatta->";
+                    CHECK(write_length == strlen(dummyData));
+                    REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                    AND_WHEN("The root user is logged in") {
+                        write_length = 0;
+                        memset(write_data, 0, sizeof(write_data));
+
+                        ret = shellmatta_auth_login(handle, 3);
+                        CHECK(ret == SHELLMATTA_OK);
+
+                        AND_WHEN("The help command is called") {
+
+                            ret = shellmatta_processData(handle, (char*)"help\r", 5);
+                            CHECK(ret == SHELLMATTA_OK);
+
+                            THEN("all commands are shown including the additional command") {
+                                char *dummyData =   (char*) "help\r\n"
+                                                            "additional  a\r\n"
+                                                            "help        ?   help [command] - print help or usage information\r\n"
+                                                            "login       li  Login command\r\n"
+                                                            "logout      lo  Logout command\r\n"
+                                                            "private     r\r\n"
+                                                            "public      p\r\n"
+                                                            "\r\n"
+                                                            "root@shellmatta->";
+                                CHECK(write_length == strlen(dummyData));
+                                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+shellmatta_retCode_t customLogin(const uint32_t userId, const char *password)
+{
+    if ((userId == 1) && (0 == strcmp(password, "123456789")))
+    {
+        return SHELLMATTA_OK;
+    }
+    else if ((userId == 2) && (0 == strcmp(password, "876543210")))
+    {
+        return SHELLMATTA_OK;
+    }
+
+    return SHELLMATTA_ERROR;
+}
+
+void logFct(const uint32_t userId, shellmatta_auth_log_event_t event)
+{
+    logUserId = userId;
+    logEvent = event;
+}
+
+SCENARIO("Check custom login") {
+    GIVEN("An initialized shellmatta instance with initialized auth with custom login and log") {
+
+        TEST_SHELLMATTA_SETUP;
+
+        shellmatta_auth_user_t userList[] = {
+            {1, false, "shimatta", NULL},
+            {2, false, "not_shimatta", NULL}
+        };
+
+        uint32_t privateCmdPerms[] = {1};
+        shellmatta_auth_perm_t permList[] = {
+            {"private", privateCmdPerms, sizeof(privateCmdPerms)/sizeof(privateCmdPerms[0])}
+        };
+
+        shellmatta_auth_init(handle, userList, 2, permList, 1, false, customLogin, logFct);
+
+        WHEN("The user shimatta logs in interactively using the correct credentials") {
+
+            ret = shellmatta_processData(handle, (char*)"login\r"
+                                                        "shimatta\r"
+                                                        "123456789\r", 25);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("The login message is printed - password is hidden") {
+                char *dummyData =   (char*) "login\r\n"
+                                            "enter username:\r\n"
+                                            "shimatta\r\n"
+                                            "enter password:\r\n"
+                                            "\r\n"
+                                            "login successful\r\n"
+                                            "\r\n"
+                                            "shimatta@shellmatta->";
+
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                AND_THEN("The shimatta user is logged in") {
+                    REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle));
+
+                    AND_WHEN("The shimatta user logs out") {
+                        ret = shellmatta_processData(handle, (char*)"logout\r", 7);
+                        CHECK(ret == SHELLMATTA_OK);
+                        THEN("The logout event is logged") {
+                            CHECK(1 == logUserId);
+                            REQUIRE(SHELLMATTA_AUTH_EVENT_LOGOUT == logEvent);
+                        }
+                    }
+
+                }
+
+                AND_THEN("The login event is logged") {
+                    CHECK(1 == logUserId);
+                    REQUIRE(SHELLMATTA_AUTH_EVENT_LOGIN == logEvent);
+                }
+            }
+        }
+
+        WHEN("The user shimatta logs in interactively using the wrong credentials") {
+
+            ret = shellmatta_processData(handle, (char*)"login\r"
+                                                        "shimatta\r"
+                                                        "12345678\r", 24);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("Login error message is printed") {
+                char *dummyData =   (char*) "login\r\n"
+                                            "enter username:\r\n"
+                                            "shimatta\r\n"
+                                            "enter password:\r\n"
+                                            "\r\n"
+                                            "username or password is wrong\r\n"
+                                            "\r\n"
+                                            "shellmatta->";
+
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+
+                AND_THEN("The shimatta user is not logged in") {
+                    REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+
+                AND_THEN("The failed login event is logged") {
+                    CHECK(1 == logUserId);
+                    REQUIRE(SHELLMATTA_AUTH_EVENT_LOGIN_FAILED == logEvent);
+                }
+            }
+        }
+    }
+}
+
+SCENARIO("Check custom login with custom login function") {
+    GIVEN("An initialized shellmatta instance with initialized auth with custom login and log") {
+
+        TEST_SHELLMATTA_SETUP;
+
+        shellmatta_auth_user_t userList[] = {
+            {1, false, "shimatta", NULL},
+            {2, false, "not_shimatta", NULL}
+        };
+
+        uint32_t privateCmdPerms[] = {1};
+        shellmatta_auth_perm_t permList[] = {
+            {"private", privateCmdPerms, sizeof(privateCmdPerms)/sizeof(privateCmdPerms[0])}
+        };
+
+        shellmatta_auth_init(handle, userList, 2, permList, 1, true, customLogin, logFct);
+
+        WHEN("The help command is called") {
+
+            ret = shellmatta_processData(handle, (char*)"help\r", 5);
+            CHECK(ret == SHELLMATTA_OK);
+
+            THEN("There is no login command") {
+                char *dummyData =   (char*) "help\r\n"
+                                            "help    ?   help [command] - print help or usage information\r\n"
+                                            "logout  lo  Logout command\r\n"
+                                            "public  p\r\n"
+                                            "\r\n"
+                                            "shellmatta->";
+
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData));
+            }
+        }
+    }
+}
+
+SCENARIO("Check if passwords can be changed") {
+    GIVEN("An initialized shellmatta instance with initialized auth") {
+
+        TEST_SHELLMATTA_SETUP;
+
+        TEST_SHELLMATTA_AUTH_SETUP;
+
+        WHEN("The password of the shellmatta user is changed") {
+
+            ret = shellmatta_auth_chpasswd(handle, "shimatta", "new_password");
+            CHECK(ret == SHELLMATTA_OK);
+
+            AND_WHEN("The user shimatta logs in passing the new credentials") {
+
+                ret = shellmatta_processData(handle, (char*)"login -u shimatta -p new_password\r", 34);
+                CHECK(ret == SHELLMATTA_OK);
+
+                THEN("The shimatta user is logged in") {
+                    REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+
+            }
+
+            AND_WHEN("The user shimatta logs in passing the old credentials") {
+
+                ret = shellmatta_processData(handle, (char*)"login -u shimatta -p 12345678\r", 30);
+                CHECK(ret == SHELLMATTA_OK);
+
+                THEN("The shimatta user is not logged in") {
+                    REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+            }
+        }
+    }
+}
+
+SCENARIO("Use functions with wrong parameters") {
+    GIVEN("An initialized shellmatta instance with initialized auth") {
+
+        TEST_SHELLMATTA_SETUP;
+
+        TEST_SHELLMATTA_AUTH_SETUP;
+
+        WHEN("Trying to log in user 0") {
+
+            ret = shellmatta_auth_login(handle, 0);
+
+            THEN("Shellmatta returns SHELLMATTA_ERROR") {
+                CHECK(ret == SHELLMATTA_ERROR);
+                AND_THEN("No user is logged in") {
+                    REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle));
+                }
+            }
+        }
+    }
+}

+ 11 - 0
test/integrationtest_auth/test_main.cpp

@@ -0,0 +1,11 @@
+// 010-TestCase.cpp
+
+// Let Catch provide main():
+#define CATCH_CONFIG_MAIN
+
+#include "test/framework/catch.hpp"
+extern "C" {
+#include "test/framework/fff.h"
+DEFINE_FFF_GLOBALS
+}
+

+ 15 - 15
test/unittest/shellmatta_utils/test_utils_shellItoa.cpp

@@ -2,51 +2,51 @@
 #include "src/shellmatta_utils.c"
 #include "src/shellmatta_utils.c"
 #include <string.h>
 #include <string.h>
 
 
-TEST_CASE( "shellmatta_utils.c - itoa - 123456 base 10" ) {
+TEST_CASE( "shellmatta_utils.c - itoa - 123456 base 10") {
     char buffer[64];
     char buffer[64];
     memset(buffer, 0, sizeof(buffer));
     memset(buffer, 0, sizeof(buffer));
-    CHECK( utils_shellItoa(123456, buffer, 10) == 6 );
-    REQUIRE( strcmp(buffer, "123456") == 0);
+    CHECK(utils_shellItoa(123456, buffer, 10) == 6);
+    REQUIRE_THAT(buffer, Catch::Matchers::Equals("123456"));
 }
 }
 
 
 TEST_CASE( "shellmatta_utils.c - itoa - 0x0ABBCCDD base 16") {
 TEST_CASE( "shellmatta_utils.c - itoa - 0x0ABBCCDD base 16") {
     char buffer[64];
     char buffer[64];
     memset(buffer, 0, sizeof(buffer));
     memset(buffer, 0, sizeof(buffer));
-    CHECK( utils_shellItoa(0x0ABBCCDD, buffer, 16) == 7 );
-    REQUIRE( strcmp(buffer, "ABBCCDD") == 0);
+    CHECK(utils_shellItoa(0x0ABBCCDD, buffer, 16) == 7);
+    REQUIRE_THAT(buffer, Catch::Matchers::Equals("ABBCCDD"));
 }
 }
 
 
 TEST_CASE( "shellmatta_utils.c - itoa - -574236 base 10") {
 TEST_CASE( "shellmatta_utils.c - itoa - -574236 base 10") {
     char buffer[64];
     char buffer[64];
     memset(buffer, 0, sizeof(buffer));
     memset(buffer, 0, sizeof(buffer));
-    CHECK( utils_shellItoa(-574236, buffer, 10) == 7 );
-    REQUIRE( strcmp(buffer, "-574236") == 0);
+    CHECK(utils_shellItoa(-574236, buffer, 10) == 7);
+    REQUIRE_THAT(buffer, Catch::Matchers::Equals("-574236"));
 }
 }
 
 
 TEST_CASE( "shellmatta_utils.c - itoa - 0x80000000 base 2") {
 TEST_CASE( "shellmatta_utils.c - itoa - 0x80000000 base 2") {
     char buffer[64];
     char buffer[64];
     memset(buffer, 0, sizeof(buffer));
     memset(buffer, 0, sizeof(buffer));
-    CHECK( utils_shellItoa(0x80000000, buffer, 2) == 33 );
-    REQUIRE( strcmp(buffer, "-10000000000000000000000000000000") == 0);
+    CHECK(utils_shellItoa(0x80000000, buffer, 2) == 33);
+    REQUIRE_THAT(buffer, Catch::Matchers::Equals("-10000000000000000000000000000000"));
 }
 }
 
 
 TEST_CASE( "shellmatta_utils.c - itoa - 0x7FFFFFFF base 2") {
 TEST_CASE( "shellmatta_utils.c - itoa - 0x7FFFFFFF base 2") {
     char buffer[64];
     char buffer[64];
     memset(buffer, 0, sizeof(buffer));
     memset(buffer, 0, sizeof(buffer));
-    CHECK( utils_shellItoa(0x7FFFFFFF, buffer, 2) == 31 );
-    REQUIRE( strcmp(buffer, "1111111111111111111111111111111") == 0);
+    CHECK(utils_shellItoa(0x7FFFFFFF, buffer, 2) == 31);
+    REQUIRE_THAT(buffer, Catch::Matchers::Equals("1111111111111111111111111111111"));
 }
 }
 
 
 TEST_CASE( "shellmatta_utils.c - itoa - 0x7FFFFFFF base 1 - wrong base") {
 TEST_CASE( "shellmatta_utils.c - itoa - 0x7FFFFFFF base 1 - wrong base") {
     char buffer[64];
     char buffer[64];
     memset(buffer, 0, sizeof(buffer));
     memset(buffer, 0, sizeof(buffer));
-    CHECK( utils_shellItoa(0x7FFFFFFF, buffer, 1) == 0 );
-    REQUIRE( strcmp(buffer, "\0") == 0);
+    CHECK(utils_shellItoa(0x7FFFFFFF, buffer, 1) == 0);
+    REQUIRE_THAT(buffer, Catch::Matchers::Equals("\0"));
 }
 }
 
 
 TEST_CASE( "shellmatta_utils.c - itoa - 0x7FFFFFFF base 17 - wrong base") {
 TEST_CASE( "shellmatta_utils.c - itoa - 0x7FFFFFFF base 17 - wrong base") {
     char buffer[64];
     char buffer[64];
     memset(buffer, 0, sizeof(buffer));
     memset(buffer, 0, sizeof(buffer));
-    CHECK( utils_shellItoa(0x7FFFFFFF, buffer, 17) == 0 );
-    REQUIRE( strcmp(buffer, "\0") == 0);
+    CHECK(utils_shellItoa(0x7FFFFFFF, buffer, 17) == 0);
+    REQUIRE_THAT(buffer, Catch::Matchers::Equals("\0"));
 }
 }

+ 2 - 2
test/unittest/shellmatta_utils/test_utils_writeEcho.cpp

@@ -17,7 +17,7 @@ static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
 TEST_CASE( "shellmatta_writeEcho echo enabled" ) {
 TEST_CASE( "shellmatta_writeEcho echo enabled" ) {
 
 
     shellmatta_instance_t inst;
     shellmatta_instance_t inst;
-    char buffer[20];
+    char buffer[20] = {0};
     char dummyData[29] = "asd";
     char dummyData[29] = "asd";
 
 
     inst.buffer = buffer;
     inst.buffer = buffer;
@@ -42,7 +42,7 @@ TEST_CASE( "shellmatta_writeEcho echo enabled" ) {
 TEST_CASE( "shellmatta_writeEcho echo disabled" ) {
 TEST_CASE( "shellmatta_writeEcho echo disabled" ) {
 
 
     shellmatta_instance_t inst;
     shellmatta_instance_t inst;
-    char buffer[20];
+    char buffer[20] = {0};
     char dummyData[29] = "asd";
     char dummyData[29] = "asd";
 
 
     inst.buffer = buffer;
     inst.buffer = buffer;