Browse Source

Merge branch 'feature/#63-add-simple-authentication-mechanism' of shimatta/shellmatta into develop

Finished authentication including integration tests.
shimatta 9 months ago
parent
commit
67279a17cb

+ 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
 
 

+ 138 - 37
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
@@ -116,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
  */
  */
@@ -126,48 +186,66 @@ typedef struct shellmatta_cmd
     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 callack 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;
 
 
+
 /**
 /**
  * @brief structure of one shellmatta instance
  * @brief structure of one shellmatta instance
  */
  */
 typedef struct
 typedef struct
 {
 {
-    uint32_t            magic;              /**< magic number to check if initialized   */
-    char                *buffer;            /**< input buffer                           */
-    uint32_t            bufferSize;         /**< size of the input buffer               */
-    uint32_t            inputCount;         /**< offset of the current write operation  */
-    uint32_t            byteCounter;        /**< counter used to loop over input data   */
-    uint32_t            lastNewlineIdx;     /**< index of the lest newline              */
-    uint32_t            cursor;             /**< offset where the cursor is at          */
-    uint32_t            stdinIdx;           /**< start index of stdin in buffer         */
-    uint32_t            stdinLength;        /**< length of the stdin data               */
-    char                *historyBuffer;     /**< buffer to store the last commands      */
-    uint32_t            historyBufferSize;  /**< size of the history buffer             */
-    uint32_t            historyStart;       /**< index of the oldest stored command     */
-    uint32_t            historyEnd;         /**< index of the newest stored command     */
-    uint32_t            historyRead;        /**< index of the current search            */
-    bool                historyReadUp;      /**< flag to show the last history dir      */
-    uint32_t            tabCounter;         /**< counts the tabulator key presses       */
-    uint32_t            escapeCounter;      /**< counts the characters of an escape seq */
-    char                escapeChars[4u];    /**< buffer to save the escape characters   */
-    uint32_t            hereStartIdx;       /**< heredoc start of "<<"                  */
-    uint32_t            hereDelimiterIdx;   /**< heredoc delimiter index in input       */
-    uint32_t            hereLength;         /**< length of the heredoc delimiter        */
-    bool                echoEnabled;        /**< if true the input is printed           */
-    bool                dirty;              /**< dirty flag to show changes             */
-    const char          *prompt;            /**< prompt is printed after every command  */
-    char                delimiter;          /**< delimiter (return) to terminate a cmd  */
-    shellmatta_mode_t   mode;               /**< mode of the shell                      */
-    shellmatta_write_t  write;              /**< pointer to write function              */
-    shellmatta_cmd_t    helpCmd;            /**< help command structure                 */
-    shellmatta_cmd_t    *cmdList;           /**< pointer to the first command           */
-    shellmatta_cmd_t    *continuousCmd;     /**< command to be called continuously      */
-    shellmatta_cmd_t    *busyCmd;           /**< command to be polled (busy mode)       */
-    bool                cmdListIsConst;     /**< true if the #cmdList was passed during
-                                                 initialization                         */
-    shellmatta_opt_t    optionParser;       /**< option parser sructure                 */
+    uint32_t            magic;                  /**< magic number to check if initialized   */
+    char                *buffer;                /**< input buffer                           */
+    uint32_t            bufferSize;             /**< size of the input buffer               */
+    uint32_t            inputCount;             /**< offset of the current write operation  */
+    uint32_t            byteCounter;            /**< counter used to loop over input data   */
+    uint32_t            lastNewlineIdx;         /**< index of the lest newline              */
+    uint32_t            cursor;                 /**< offset where the cursor is at          */
+    uint32_t            stdinIdx;               /**< start index of stdin in buffer         */
+    uint32_t            stdinLength;            /**< length of the stdin data               */
+    char                *historyBuffer;         /**< buffer to store the last commands      */
+    uint32_t            historyBufferSize;      /**< size of the history buffer             */
+    uint32_t            historyStart;           /**< index of the oldest stored command     */
+    uint32_t            historyEnd;             /**< index of the newest stored command     */
+    uint32_t            historyRead;            /**< index of the current search            */
+    bool                historyReadUp;          /**< flag to show the last history dir      */
+    uint32_t            tabCounter;             /**< counts the tabulator key presses       */
+    uint32_t            escapeCounter;          /**< counts the characters of an escape seq */
+    char                escapeChars[4u];        /**< buffer to save the escape characters   */
+    uint32_t            hereStartIdx;           /**< heredoc start of "<<"                  */
+    uint32_t            hereDelimiterIdx;       /**< heredoc delimiter index in input       */
+    uint32_t            hereLength;             /**< length of the heredoc delimiter        */
+    bool                echoEnabled;            /**< if true the input is printed           */
+    bool                dirty;                  /**< dirty flag to show changes             */
+    const char          *prompt;                /**< prompt is printed after every command  */
+    char                delimiter;              /**< delimiter (return) to terminate a cmd  */
+    shellmatta_mode_t   mode;                   /**< mode of the shell                      */
+    shellmatta_write_t  write;                  /**< pointer to write function              */
+    shellmatta_cmd_t    helpCmd;                /**< help command structure                 */
+    shellmatta_cmd_t    *cmdList;               /**< pointer to the first command           */
+    shellmatta_cmd_t    *continuousCmd;         /**< command to be called continuously      */
+    shellmatta_cmd_t    *busyCmd;               /**< command to be polled (busy mode)       */
+    bool                cmdListIsConst;         /**< true if the #cmdList was passed during
+                                                     initialization                         */
+    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
 } shellmatta_instance_t;
 } shellmatta_instance_t;
 
 
 
 
@@ -187,8 +265,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,
@@ -226,6 +304,29 @@ shellmatta_retCode_t shellmatta_printf(     shellmatta_handle_t handle,
                                             SHELLMATTA_ATTR_FORMAT(2, 3);
                                             SHELLMATTA_ATTR_FORMAT(2, 3);
 #endif
 #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
 
 
 /** @} */
 /** @} */

+ 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>

+ 10 - 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
@@ -32,4 +36,10 @@
         end
         end
     @enduml
     @enduml
 
 
+    @section shellmatta_options Shellmatta options
+
+    The Shellmatta comes with some features which can be opted in:
+
+    @subpage shellmatta_auth
+
 */
 */

+ 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;

+ 83 - 34
makefile

@@ -1,16 +1,25 @@
-# 
+#
 # Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
 # Copyright (c) 2019 - 2021 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/
 OBJ_DIR := output/
+OBJ_DIR_EXAMPLE := $(OBJ_DIR)example_obj/
 INTEGRATIONTEST_CPP_OBJ_DIR := $(OBJ_DIR)test/integrationtest/
 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_C_OBJ_DIR := $(INTEGRATIONTEST_CPP_OBJ_DIR)
+INTEGRATIONTEST_AUTH_C_OBJ_DIR := $(INTEGRATIONTEST_AUTH_CPP_OBJ_DIR)
 UNITTEST_OBJ_DIR := $(OBJ_DIR)test/unittest/
 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++
 LN  := ln
 LN  := ln
@@ -22,6 +31,20 @@ SOURCES :=  src/shellmatta.c                \
             src/shellmatta_escape.c         \
             src/shellmatta_escape.c         \
             src/shellmatta_opt.c
             src/shellmatta_opt.c
 
 
+AUTH_SOURCES     := $(SOURCES) src/shellmatta_auth.c
+EXAMPLE_SOURCES  := $(AUTH_SOURCES) example/main.c
+EXAMPLE_COBJ     := $(patsubst %.c,$(OBJ_DIR_EXAMPLE)%.o,$(EXAMPLE_SOURCES))
+
+CPPCHECK_SOURCES := $(AUTH_SOURCES)
+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 .
 
 
 UNITTEST_SOURCES := test/unittest/test_main.cpp                                         \
 UNITTEST_SOURCES := test/unittest/test_main.cpp                                         \
@@ -52,29 +75,41 @@ INTEGRATIONTEST_SOURCES :=  test/integrationtest/test_main.cpp
                             test/integrationtest/test_integration_history.cpp   \
                             test/integrationtest/test_integration_history.cpp   \
                             test/integrationtest/test_integration_help.cpp
                             test/integrationtest/test_integration_help.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))
 
 
 INTEGRATIONTEST_CPPOBJ  :=  $(patsubst %.cpp,$(INTEGRATIONTEST_CPP_OBJ_DIR)%.o,$(INTEGRATIONTEST_SOURCES))
 INTEGRATIONTEST_CPPOBJ  :=  $(patsubst %.cpp,$(INTEGRATIONTEST_CPP_OBJ_DIR)%.o,$(INTEGRATIONTEST_SOURCES))
 INTEGRATIONTEST_COBJ    :=  $(patsubst %.c,$(INTEGRATIONTEST_CPP_OBJ_DIR)%.o,$(SOURCES))
 INTEGRATIONTEST_COBJ    :=  $(patsubst %.c,$(INTEGRATIONTEST_CPP_OBJ_DIR)%.o,$(SOURCES))
 
 
+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 -DSHELLMATTA_HELP_ALIAS=\(char*\)\"?\"
 CFLAGS      := $(INCLUDES:%=-I%) -g -Wall -Werror -Wextra -pedantic -DSHELLMATTA_HELP_ALIAS=\(char*\)\"?\"
+CFLAGS_EXAMPLE = $(CFLAGS) -DSHELLMATTA_AUTHENTICATION
 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
 
 
 DEPEND      = -MT $@ -MF "$(@:%.o=%.d)" -MG -MM
 DEPEND      = -MT $@ -MF "$(@:%.o=%.d)" -MG -MM
 
 
 COBJ    := $(patsubst %.c,$(OBJ_DIR)%.o,$(SOURCES))
 COBJ    := $(patsubst %.c,$(OBJ_DIR)%.o,$(SOURCES))
 
 
-EXAMPLE_SOURCES := example/main.c
-EXAMPLE_COBJ     := $(patsubst %.c,$(OBJ_DIR)%.o,$(EXAMPLE_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_TARGET_AUTH     := $(OBJ_DIR)test/integrationtest_auth/integrationtest_auth
+
+TEST_RUN_TARGETS := unittest integrationtest integrationtest_auth
+# 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)
 
 
-OBJ     := $(COBJ) $(EXAMPLE_COBJ) $(UNITTEST_CPPOBJ) $(INTEGRATIONTEST_CPPOBJ) $(INTEGRATIONTEST_COBJ)
+OBJ     := $(COBJ) $(EXAMPLE_COBJ) $(UNITTEST_CPPOBJ) $(INTEGRATIONTEST_CPPOBJ) $(INTEGRATIONTEST_COBJ) $(INTEGRATIONTEST_AUTH_CPPOBJ) $(INTEGRATIONTEST_AUTH_COBJ)
 DEPS    := $(OBJ:%.o=%.d)
 DEPS    := $(OBJ:%.o=%.d)
 
 
 export
 export
@@ -89,35 +124,34 @@ help:
 	@echo example   - build example
 	@echo example   - build example
 	@echo -----------------------------------------------
 	@echo -----------------------------------------------
 
 
-test: unittest integrationtest
+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
+	@-find . -name "*.gcda" -type f -delete
+	$(foreach TEST_RUN_TARGET,$(TEST_RUN_TARGETS),./$(OBJ_DIR)test/$(TEST_RUN_TARGET)/$(TEST_RUN_TARGET)${\n})
+
+#	remove report from former run
+	-rm -rf $(OBJ_DIR)test/report/*
+	gcovr --html-details --output $(OBJ_DIR)test/report/report.html -f src -f api -d
 
 
 cppcheck:
 cppcheck:
 	- @mkdir -p output/cppcheck/html
 	- @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 $(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
 	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_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
 	@-find . -name "*.gcda" -type f -delete
-	-$(UNITTEST_TARGET)
+	-$(OBJ_DIR)test/$@/$@
 
 
 #	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
-
-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)
-
-	#	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
+	-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
@@ -126,26 +160,31 @@ 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)
 
 
-$(EXAMPLE_TARGET): $(COBJ) $(EXAMPLE_COBJ)
+$(EXAMPLE_TARGET): $(EXAMPLE_COBJ)
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
 	$(CC) $(LFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
 	$(CC) $(LFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
 
 
 $(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_TARGET_AUTH): $(INTEGRATIONTEST_AUTH_CPPOBJ) $(INTEGRATIONTEST_AUTH_COBJ)
+	- @mkdir -p $(@D)
+	echo askdjhaskjdhskj
+	$(CPP) $(TESTLFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
+
 $(TARGET): $(OBJ)
 $(TARGET): $(OBJ)
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
 	$(CC) $(LFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
 	$(CC) $(LFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
-	
+
 $(COBJ):
 $(COBJ):
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
 	@$(CC) -c $(CFLAGS) $(DEPEND) -o $(@:%.o=%.d) $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
 	@$(CC) -c $(CFLAGS) $(DEPEND) -o $(@:%.o=%.d) $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
@@ -153,8 +192,8 @@ $(COBJ):
 
 
 $(EXAMPLE_COBJ):
 $(EXAMPLE_COBJ):
 	- @mkdir -p $(@D)
 	- @mkdir -p $(@D)
-	@$(CC) -c $(CFLAGS) $(DEPEND) -o $(@:%.o=%.d) $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
-	$(CC) -c $(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)
@@ -171,6 +210,16 @@ $(INTEGRATIONTEST_COBJ):
 	@$(CC) -c $(TESTFLAGS) $(DEPEND) -o $(@:%.o=%.d) $(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_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 $(@:%.o=%.d) $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
 	@$(CPP) -c $(CFLAGS) $(DEPEND) -o $(@:%.o=%.d) $(subst $(OBJ_DIR), ,$(@:%.o=%.c))

+ 51 - 21
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
@@ -23,6 +23,9 @@
 #include "shellmatta_utils.h"
 #include "shellmatta_utils.h"
 #include "shellmatta_escape.h"
 #include "shellmatta_escape.h"
 #include "shellmatta_opt.h"
 #include "shellmatta_opt.h"
+#ifdef SHELLMATTA_AUTHENTICATION
+#include "shellmatta_auth.h"
+#endif
 #include <stddef.h>
 #include <stddef.h>
 #include <string.h>
 #include <string.h>
 #include <stdarg.h>
 #include <stdarg.h>
@@ -71,44 +74,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;
         }
         }
 
 
@@ -132,7 +130,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;
@@ -161,6 +159,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 */
@@ -252,6 +255,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
     {
     {
@@ -268,7 +287,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;
@@ -608,14 +627,25 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t     handle,
                         {
                         {
                             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;

+ 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;
     inst->write("\r\n", 2u);
     inst->write("\r\n", 2u);
+#ifdef SHELLMATTA_AUTHENTICATION
+    inst->loginState        = SHELLMATTA_AUTH_IDLE;
+    if (NULL != inst->userPointer)
+    {
+        inst->write(inst->userPointer->username, strlen(inst->userPointer->username));
+        inst->write("@", 1);
+    }
+#endif
     inst->write(inst->prompt, strlen(inst->prompt));
     inst->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
@@ -113,7 +113,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,

+ 29 - 36
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" ) {
@@ -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"));
 }
 }