Browse Source

Merge branch 'develop' into feature/add_ymodem_receive_functionalities

Strobel, Stefan | Friedrich Lütze GmbH 1 year ago
parent
commit
a3415d2a55
51 changed files with 5094 additions and 1489 deletions
  1. 1 0
      .gitignore
  2. 42 0
      .vscode/launch.json
  3. 3 9
      .vscode/settings.json
  4. 16 0
      .vscode/tasks.json
  5. 9 7
      README.md
  6. 129 13
      api/shellmatta.h
  7. 72 0
      cfg/cppcheck/cppcheck_suppressions.xml
  8. 1 1
      cfg/doxygen/doxyfile
  9. 10 0
      doc/shellmatta.dox
  10. 84 0
      doc/shellmatta_auth.dox
  11. 24 2
      doc/shellmatta_transport_layer.dox
  12. 24 9
      example/main.c
  13. 122 68
      makefile
  14. 17 10
      python_driver/shellmatta_transport.py
  15. 5 9
      python_driver/shellmatta_transport_serial.py
  16. 76 39
      src/shellmatta.c
  17. 634 0
      src/shellmatta_auth.c
  18. 38 0
      src/shellmatta_auth.h
  19. 25 1
      src/shellmatta_autocomplete.c
  20. 1 1
      src/shellmatta_autocomplete.h
  21. 1 1
      src/shellmatta_crc.c
  22. 1 1
      src/shellmatta_crc.h
  23. 1 1
      src/shellmatta_escape.c
  24. 1 1
      src/shellmatta_escape.h
  25. 16 3
      src/shellmatta_history.c
  26. 2 1
      src/shellmatta_history.h
  27. 3 3
      src/shellmatta_opt.c
  28. 1 1
      src/shellmatta_opt.h
  29. 39 21
      src/shellmatta_transport.c
  30. 22 11
      src/shellmatta_transport.h
  31. 33 3
      src/shellmatta_utils.c
  32. 3 3
      src/shellmatta_utils.h
  33. 5 4
      src/shellmatta_ymodem.c
  34. 2282 1171
      test/framework/catch.hpp
  35. 31 38
      test/integrationtest/test_integration.cpp
  36. 3 3
      test/integrationtest/test_integration_busy.cpp
  37. 5 5
      test/integrationtest/test_integration_continue.cpp
  38. 9 9
      test/integrationtest/test_integration_help.cpp
  39. 1 1
      test/integrationtest/test_integration_history.cpp
  40. 3 3
      test/integrationtest/test_integration_opt.cpp
  41. 3 3
      test/integrationtest/test_integration_optLong.cpp
  42. 847 0
      test/integrationtest_auth/test_integration_auth.cpp
  43. 11 0
      test/integrationtest_auth/test_main.cpp
  44. 126 15
      test/integrationtest/test_integration_transport.cpp
  45. 11 0
      test/integrationtest_transport/test_main.cpp
  46. 9 1
      test/unittest/shellmatta_crc/test_crc32Fast.cpp
  47. 8 0
      test/unittest/shellmatta_crc/test_crc32Slow.cpp
  48. 265 0
      test/unittest/shellmatta_crc/test_crc32_data.h
  49. 15 15
      test/unittest/shellmatta_utils/test_utils_shellItoa.cpp
  50. 2 2
      test/unittest/shellmatta_utils/test_utils_writeEcho.cpp
  51. 2 0
      test/unittest/test_main.cpp

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
+output
 **__pycache__**
 **__pycache__**
 .cache
 .cache
 compile_commands.json
 compile_commands.json

+ 42 - 0
.vscode/launch.json

@@ -66,6 +66,48 @@
             ],
             ],
             "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"
+        },
+        {
+            "name": "debug integrationtest_transport",
+            "type": "cppdbg",
+            "request": "launch",
+            "program": "${workspaceFolder}/output/test/integrationtest_transport/integrationtest_transport",
+            "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_transport",
+            "miDebuggerPath": "/usr/bin/gdb"
         }
         }
     ]
     ]
 }
 }

+ 3 - 9
.vscode/settings.json

@@ -1,13 +1,7 @@
 {
 {
     "files.associations": {
     "files.associations": {
-        "random": "cpp",
-        "*.tcc": "cpp",
-        "string": "cpp",
-        "vector": "cpp",
-        "fstream": "cpp",
-        "limits": "cpp",
         "sstream": "cpp",
         "sstream": "cpp",
-        "utility": "cpp",
-        "algorithm": "cpp"
-    }
+        "regex": "cpp"
+    },
+    "C_Cpp.default.defines": ["SHELLMATTA_AUTH", "SHELLMATTA_TRANSPORT"]
 }
 }

+ 16 - 0
.vscode/tasks.json

@@ -38,6 +38,22 @@
                 "$gcc"
                 "$gcc"
             ]
             ]
         },
         },
+        {
+            "label": "make integrationtest_auth",
+            "type": "shell",
+            "command": "make integrationtest_auth",
+            "problemMatcher": [
+                "$gcc"
+            ]
+        },
+        {
+            "label": "make integrationtest_transport",
+            "type": "shell",
+            "command": "make integrationtest_transport",
+            "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
 
 

+ 129 - 13
api/shellmatta.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  *
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -25,6 +25,21 @@
 
 
 /* global defines */
 /* global defines */
 
 
+
+/*
+ * Define the printf format specifier for all GCC versions > 3.3
+ * This will let the compiler know that shellmatta_printf() is a function taking printf-like format specifiers.
+ */
+#ifndef SHELLMATTA_ATTR_FORMAT
+#   if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)))
+#       define SHELLMATTA_ATTR_FORMAT(fmt, args) __attribute__((__format__(__printf__, fmt, args)))
+#   else
+#       define SHELLMATTA_ATTR_FORMAT(fmt, args)
+#   endif
+#else
+#   define SHELLMATTA_ATTR_FORMAT(fmt, args)
+#endif
+
 /**
 /**
  * @brief definition of a shellmatta handle
  * @brief definition of a shellmatta handle
  */
  */
@@ -101,6 +116,66 @@ typedef shellmatta_retCode_t (*shellmatta_cmdFct_t)(const shellmatta_handle_t
  */
  */
 typedef shellmatta_retCode_t (*shellmatta_write_t)(const char* data, uint32_t length);
 typedef shellmatta_retCode_t (*shellmatta_write_t)(const char* data, uint32_t length);
 
 
+#ifdef SHELLMATTA_AUTHENTICATION
+/**
+ * @brief user role matrix
+ */
+typedef struct
+{
+    uint32_t    userId;     /**< id of the user (!= 0)                              */
+    bool        superuser;  /**< allow the user to access all commands              */
+    const char  *username;  /**< name of the user role                              */
+    const char  *password;  /**< password of the user role or NULL (custom auth)    */
+} shellmatta_auth_user_t;
+
+/**
+ * @brief typedefinition of one line of the authentication table
+ */
+typedef struct
+{
+    const char*    cmd;           /**< command to grand access to               */
+    const uint32_t *userIds;      /**< list of user ids with access to the cmd  */
+    const uint32_t userIdslength; /**< length of the user list                  */
+} shellmatta_auth_perm_t;
+
+/**
+ * @brief login states
+ */
+typedef enum
+{
+    SHELLMATTA_AUTH_IDLE,       /**< authentication system waits    */
+    SHELLMATTA_AUTH_USERNAME,   /**< input of username              */
+    SHELLMATTA_AUTH_PASSWORD    /**< input of password              */
+} shellmatta_auth_state_t;
+
+/**
+ * @brief log actions - passed to the log function
+ */
+typedef enum
+{
+    SHELLMATTA_AUTH_EVENT_NONE,         /**< no event (init value)      */
+    SHELLMATTA_AUTH_EVENT_LOGIN,        /**< successful login event     */
+    SHELLMATTA_AUTH_EVENT_LOGIN_FAILED, /**< failed login event         */
+    SHELLMATTA_AUTH_EVENT_LOGOUT,       /**< succesful logout event     */
+} shellmatta_auth_log_event_t;
+
+/**
+ * @brief custom shellmatta authentication method
+ * @param[in]   userId      user id to log in (name of the user role)
+ * @param[in]   password    password for the login
+ * @return      userId if password was correct - otherwise 0
+ */
+typedef shellmatta_retCode_t (*shellmatta_auth_check_t)(const uint32_t userId, const char* password);
+
+/**
+ * @brief shellmatta authentication log method - will be called whenever a login attempt is done
+ * @param[in]   userId  userId to be logged in (0 on unknown user)
+ * @param[in]   event   event type to be logged (e.g. successful login)
+ */
+typedef void (*shellmatta_auth_log_t)(const uint32_t userId, shellmatta_auth_log_event_t event);
+
+#endif
+
 /**
 /**
  * @brief structure of one shellmatta command
  * @brief structure of one shellmatta command
  */
  */
@@ -110,7 +185,10 @@ typedef struct shellmatta_cmd
     char                    *cmdAlias;  /**< command alias                          */
     char                    *cmdAlias;  /**< command alias                          */
     char                    *helpText;  /**< help text to print in "help" command   */
     char                    *helpText;  /**< help text to print in "help" command   */
     char                    *usageText; /**< usage text - printed on "help cmd"     */
     char                    *usageText; /**< usage text - printed on "help cmd"     */
-    shellmatta_cmdFct_t     cmdFct;     /**< pointer to the cmd callback function   */
+    shellmatta_cmdFct_t     cmdFct;     /**< pointer to the cmd callack function    */
+#ifdef SHELLMATTA_AUTHENTICATION
+    shellmatta_auth_perm_t  *authLink;  /**< internally used - pointer to perm list */
+#endif
     struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
     struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
 } shellmatta_cmd_t;
 } shellmatta_cmd_t;
 
 
@@ -131,7 +209,7 @@ typedef struct
     void (*ymodemTransmissionCompleteCallback)(void);
     void (*ymodemTransmissionCompleteCallback)(void);
 } shellmatta_ymodem_callbacks_t;
 } shellmatta_ymodem_callbacks_t;
 
 
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_TRANSPORT
 
 
 /**
 /**
  * @brief definition of shellmatta transport layer states
  * @brief definition of shellmatta transport layer states
@@ -191,9 +269,9 @@ typedef struct
     bool                            mandatory;          /**< is the transport layer enforced                */
     bool                            mandatory;          /**< is the transport layer enforced                */
     uint8_t                         sequenceH2S;        /**< sequence counter host to shellmatta            */
     uint8_t                         sequenceH2S;        /**< sequence counter host to shellmatta            */
     uint8_t                         sequenceS2H;        /**< sequenc counter shellmatta to host             */
     uint8_t                         sequenceS2H;        /**< sequenc counter shellmatta to host             */
-    uint32_t                        headerIndex;        /**< read indey of the header                       */
+    uint32_t                        headerIndex;        /**< read index of the header                       */
     uint32_t                        payloadIndex;       /**< read index of the payload                      */
     uint32_t                        payloadIndex;       /**< read index of the payload                      */
-    uint32_t                        crcIndex;           /**< read index of the checmsum                     */
+    uint32_t                        crcIndex;           /**< read index of the checksum                     */
     shellmatta_transport_packet_t   inPacket;           /**< buffer for the received packets                */
     shellmatta_transport_packet_t   inPacket;           /**< buffer for the received packets                */
     shellmatta_transport_packet_t   outPacket;          /**< buffer for the sent packets                    */
     shellmatta_transport_packet_t   outPacket;          /**< buffer for the sent packets                    */
     shellmatta_write_t              writeFct;           /**< shellmatta write function                      */
     shellmatta_write_t              writeFct;           /**< shellmatta write function                      */
@@ -242,7 +320,21 @@ typedef struct
                                                              initialization                         */
                                                              initialization                         */
     shellmatta_opt_t                optionParser;       /**< option parser sructure                 */
     shellmatta_opt_t                optionParser;       /**< option parser sructure                 */
     shellmatta_ymodem_state_t       ymodemState;        /**< current state of the ymodem module     */
     shellmatta_ymodem_state_t       ymodemState;        /**< current state of the ymodem module     */
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_AUTHENTICATION
+    shellmatta_auth_state_t         loginState;         /**< state variable of the login cmd        */
+    shellmatta_cmd_t                loginCmd;           /**< login command structure                */
+    shellmatta_cmd_t                logoutCmd;          /**< logout command structure               */
+    uint32_t                        userId;             /**< user ID of the current session         */
+    uint32_t                        tmpUserId;          /**< remporary user ID during input         */
+    shellmatta_auth_user_t          *userPointer;       /**< pointer to the user in the user list   */
+    shellmatta_auth_user_t          *userList;          /**< user list                              */
+    uint32_t                        userListLength;     /**< length of the user list                */
+    shellmatta_auth_perm_t          *permList;          /**< permission list                        */
+    uint32_t                        permListLength;     /**< length of the permission list          */
+    shellmatta_auth_check_t         checkFct;           /**< custom credential check function       */
+    shellmatta_auth_log_t           logFct;             /**< auth log function                      */
+#endif
+#ifdef SHELLMATTA_TRANSPORT
     bool                            transportEnabled;   /**< if true the transport layer is enabled */
     bool                            transportEnabled;   /**< if true the transport layer is enabled */
     uint32_t                        transportBusyMark;  /**< transport processing position during 
     uint32_t                        transportBusyMark;  /**< transport processing position during 
                                                              busy cmd execution                     */
                                                              busy cmd execution                     */
@@ -253,7 +345,7 @@ typedef struct
 /**
 /**
  * @brief helper macro for the send function
  * @brief helper macro for the send function
  */
  */
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_TRANSPORT
 #define SHELLMATTA_WRITE(data, length)  inst->transportLayer.active == true ?                                                                   \
 #define SHELLMATTA_WRITE(data, length)  inst->transportLayer.active == true ?                                                                   \
                                         shellmatta_transport_write((shellmatta_transport_layer_t*)&inst->transportLayer, (data), (length)) :    \
                                         shellmatta_transport_write((shellmatta_transport_layer_t*)&inst->transportLayer, (data), (length)) :    \
                                         inst->write((data), (length))
                                         inst->write((data), (length))
@@ -277,8 +369,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,
@@ -309,23 +401,47 @@ shellmatta_retCode_t shellmatta_opt_long(   shellmatta_handle_t         handle,
                                             char                        **argument,
                                             char                        **argument,
                                             uint32_t                    *argLen);
                                             uint32_t                    *argLen);
 
 
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_TRANSPORT
 
 
 shellmatta_retCode_t shellmatta_transport_configure(shellmatta_handle_t         handle,
 shellmatta_retCode_t shellmatta_transport_configure(shellmatta_handle_t         handle,
                                                     bool                        mandatory,
                                                     bool                        mandatory,
                                                     bool                        disableAutoFlush,
                                                     bool                        disableAutoFlush,
                                                     shellmatta_transport_crc_t  customCrcFct);
                                                     shellmatta_transport_crc_t  customCrcFct);
 
 
-shellmatta_retCode_t shellmatta_transport_reset(shellmatta_handle_t     handle);
+shellmatta_retCode_t shellmatta_transport_reset(shellmatta_handle_t handle);
 
 
-shellmatta_retCode_t shellmatta_transport_flush(shellmatta_handle_t     handle);
+shellmatta_retCode_t shellmatta_transport_flush(shellmatta_handle_t handle);
 
 
 #endif
 #endif
 
 
 #ifndef SHELLMATTA_STRIP_PRINTF
 #ifndef SHELLMATTA_STRIP_PRINTF
 shellmatta_retCode_t shellmatta_printf(     shellmatta_handle_t handle,
 shellmatta_retCode_t shellmatta_printf(     shellmatta_handle_t handle,
                                             const char          *fmt,
                                             const char          *fmt,
-                                            ...);
+                                            ...)
+                                            SHELLMATTA_ATTR_FORMAT(2, 3);
+#endif
+
+#ifdef SHELLMATTA_AUTHENTICATION
+
+shellmatta_retCode_t shellmatta_auth_init(                  shellmatta_handle_t     handle,
+                                                            shellmatta_auth_user_t  *userList,
+                                                            uint32_t                userListLength,
+                                                            shellmatta_auth_perm_t  *permList,
+                                                            uint32_t                permListLength,
+                                                            bool                    customLogin,
+                                                            shellmatta_auth_check_t checkFct,
+                                                            shellmatta_auth_log_t   logFct);
+shellmatta_retCode_t shellmatta_auth_login(                 shellmatta_handle_t     handle,
+                                                            uint32_t                userId);
+shellmatta_retCode_t shellmatta_auth_logout(                shellmatta_handle_t     handle);
+uint32_t             shellmatta_auth_getLoggedInUserId(     shellmatta_handle_t     handle);
+shellmatta_retCode_t shellmatta_auth_getLoggedInUserName(   shellmatta_handle_t     handle,
+                                                            char                    *data,
+                                                            uint32_t                *length);
+shellmatta_retCode_t shellmatta_auth_chpasswd(              shellmatta_handle_t     handle,
+                                                            const char              *username,
+                                                            const char              *password);
+
 #endif
 #endif
 
 
 uint8_t shellmatta_ymodem(  shellmatta_handle_t             handle,
 uint8_t shellmatta_ymodem(  shellmatta_handle_t             handle,

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

+ 1 - 1
cfg/doxygen/doxyfile

@@ -2170,7 +2170,7 @@ INCLUDE_FILE_PATTERNS  =
 # recursively expanded use the := operator instead of the = operator.
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 
-PREDEFINED             =
+PREDEFINED             = SHELLMATTA_AUTHENTICATION SHELLMATTA_TRANSPORT
 
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
 # tag can be used to specify a list of macro names that should be expanded. The

+ 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
@@ -33,6 +37,12 @@
     @enduml
     @enduml
 
 
 
 
+    @section shellmatta_options Shellmatta options
+
+    The Shellmatta comes with some features which can be opted in:
+
+    @subpage shellmatta_auth
+
     @subpage shellmatta_transport_layer
     @subpage shellmatta_transport_layer
 
 
 */
 */

+ 84 - 0
doc/shellmatta_auth.dox

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

+ 24 - 2
doc/shellmatta_transport_layer.dox

@@ -7,6 +7,10 @@
 
 
     The transport layer is optional and can be removed during compile time as
     The transport layer is optional and can be removed during compile time as
     well as deactivated during runtime.
     well as deactivated during runtime.
+
+    To enable it during compile time add the define ``SHELLMATTA_TRANSPORT``.
+
+
     The transport layer intends to be used in machine to machine interfaces.
     The transport layer intends to be used in machine to machine interfaces.
 
 
     @warning    As the transport layer is connectionless there is no way to
     @warning    As the transport layer is connectionless there is no way to
@@ -64,13 +68,12 @@
     | 0x84 | Respond to set address         | 16             | UUID of Shellmatta     |
     | 0x84 | Respond to set address         | 16             | UUID of Shellmatta     |
 
 
 
 
-
     @section shellmatta_transport_layer_sequence_counters Sequence counters
     @section shellmatta_transport_layer_sequence_counters Sequence counters
 
 
     The sequence counters are included in the header of every packet to enable
     The sequence counters are included in the header of every packet to enable
     the host to check for any dropped packets.
     the host to check for any dropped packets.
     This is a quite nasty workaround as the host has no chance to determine
     This is a quite nasty workaround as the host has no chance to determine
-    which packet has dropped - but better than nothing.
+    which packet has been dropped - but better than nothing.
 
 
     If no response is received from the shellmatta the sequence counters can be
     If no response is received from the shellmatta the sequence counters can be
     requested explicitly.
     requested explicitly.
@@ -84,14 +87,33 @@
 
 
     @section shellmatta_transport_layer_buffersize Buffer sizes
     @section shellmatta_transport_layer_buffersize Buffer sizes
 
 
+    @todo Not implemented yet
+
 
 
     @section shellmatta_transport_layer_addressing Addressing and Search
     @section shellmatta_transport_layer_addressing Addressing and Search
 
 
+    @todo Not implemented yet
+
 
 
     @section shellmatta_transport_layer_crc CRC calculation
     @section shellmatta_transport_layer_crc CRC calculation
 
 
+    The shellmatta transport layer uses the CRC32 algorithm to secure the
+    transmission (0x04c11db7).
+
+    By default the shellmatta crc implementation uses a lookup table to
+    calculate the crc in a performant way.
+
+    This uses quite a lot of read only memory on the device. If the memory
+    consumption is an Issue you can switch to a slower implementation without
+    the lookup table. This will increase CPU load quite a bit, but saves nearly
+    1K of read only memory.
+
+    To enable the lookup table less CRC just define
+    ``SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP`` during compilation.
+
 
 
     @section shellmatta_transport_layer_control Controlling the transport layer
     @section shellmatta_transport_layer_control Controlling the transport layer
 
 
+    @todo Not implemented yet
 
 
 */
 */

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

+ 122 - 68
makefile

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

+ 17 - 10
python_driver/shellmatta_transport.py

@@ -36,7 +36,8 @@ class ShellmattaTransport():
                      sequence_h2s,
                      sequence_h2s,
                      sequence_s2h,
                      sequence_s2h,
                      payload=b'',
                      payload=b'',
-                     crc=0):
+                     crc=0,
+                     crc_fct=Crc32.calc):
             """ creates a shellmatta transport packet based on the passed data """
             """ creates a shellmatta transport packet based on the passed data """
 
 
             self.start_of_header    = start_of_header
             self.start_of_header    = start_of_header
@@ -49,6 +50,7 @@ class ShellmattaTransport():
             self.sequence_s2h       = sequence_s2h
             self.sequence_s2h       = sequence_s2h
             self.payload            = payload
             self.payload            = payload
             self.crc                = crc
             self.crc                = crc
+            self.crc_fct            = crc_fct
 
 
         @classmethod
         @classmethod
         def from_header_data(cls, header_data):
         def from_header_data(cls, header_data):
@@ -76,16 +78,20 @@ class ShellmattaTransport():
             self.payload += payload
             self.payload += payload
             self.payload_length = len(self.payload)
             self.payload_length = len(self.payload)
 
 
+        def calc_crc(self):
+            """ Calculates the crc checksum """
+
+            return self.crc_fct(bytes(self)[:-4])
+
         def secure(self):
         def secure(self):
             """ Calculates the crc checksum """
             """ Calculates the crc checksum """
 
 
-            self.crc = Crc32.calc(bytes(self)[:-4])
+            self.crc = self.calc_crc()
 
 
         def verify(self, crc):
         def verify(self, crc):
             """ Checks the packet agains the passed crc """
             """ Checks the packet agains the passed crc """
 
 
-            self.secure()
-            return crc == self.crc
+            return crc == self.calc_crc()
 
 
         def __bytes__(self):
         def __bytes__(self):
             """ Create binary representation of the packet """
             """ Create binary representation of the packet """
@@ -104,7 +110,7 @@ class ShellmattaTransport():
             raw_buffer += self.crc.to_bytes(4, 'big')
             raw_buffer += self.crc.to_bytes(4, 'big')
             return raw_buffer
             return raw_buffer
 
 
-    def __init__(self, com_obj, mandatory=False, custom_crc=None):
+    def __init__(self, com_obj, mandatory=False, custom_crc=Crc32.calc):
         self.com_obj = com_obj
         self.com_obj = com_obj
         self.mandatory = mandatory
         self.mandatory = mandatory
         self.custom_crc = custom_crc
         self.custom_crc = custom_crc
@@ -130,7 +136,8 @@ class ShellmattaTransport():
                                 destination,
                                 destination,
                                 self.sequence_counter_h2s,
                                 self.sequence_counter_h2s,
                                 self.sequence_counter_s2h,
                                 self.sequence_counter_s2h,
-                                data[:self.Packet.MAX_PAYLOAD_LENGTH])
+                                data[:self.Packet.MAX_PAYLOAD_LENGTH],
+                                crc_fct=self.custom_crc)
             self.sequence_counter_h2s += 1
             self.sequence_counter_h2s += 1
             packet.secure()
             packet.secure()
             self.com_obj.write(bytes(packet))
             self.com_obj.write(bytes(packet))
@@ -250,9 +257,9 @@ class ShellmattaTransport():
         self.received_packet = None
         self.received_packet = None
         self.received_buffer = b''
         self.received_buffer = b''
         # flush the buffer and send one cancel
         # flush the buffer and send one cancel
-        # self.com_obj.write(b'\x00' * (self.Packet.MAX_PAYLOAD_LENGTH + 12))
-        # self.write(b'\x03')
+        self.com_obj.write(b'\x00' * (self.Packet.MAX_PAYLOAD_LENGTH + 12))
+        self.write(b'\x03')
 
 
     def close(self):
     def close(self):
-        """Close port"""
-        self.reset()
+        """ Close port """
+        self.reset()

+ 5 - 9
python_driver/shellmatta_transport_serial.py

@@ -4,11 +4,9 @@ import time
 import serial
 import serial
 import argparse
 import argparse
 from shellmatta_transport import ShellmattaTransport
 from shellmatta_transport import ShellmattaTransport
-from robot.api import logger
-
 
 
 class ShellmattaSerial():
 class ShellmattaSerial():
-    """ Helper class to communicate with a shelmatta enabled device """
+    """ Helper class to communicate with a shellmatta enabled device """
 
 
 
 
     def __init__(self, com_port, baudrate=115200, prompt="->", timeout=1, transport_layer_mandatory=False):
     def __init__(self, com_port, baudrate=115200, prompt="->", timeout=1, transport_layer_mandatory=False):
@@ -17,9 +15,7 @@ class ShellmattaSerial():
         self.com = com_port
         self.com = com_port
         self.com_port = serial.Serial(com_port, baudrate=baudrate, timeout=timeout)
         self.com_port = serial.Serial(com_port, baudrate=baudrate, timeout=timeout)
 
 
-        self.transport = ShellmattaTransport(self.com_port,
-                                             transport_layer_mandatory,
-                                             None)
+        self.transport = ShellmattaTransport(self.com_port, transport_layer_mandatory)
 
 
     def send_command(self, command):
     def send_command(self, command):
         """ Send command and wait for the prompt in the response. """
         """ Send command and wait for the prompt in the response. """
@@ -71,14 +67,16 @@ class ShellmattaSerial():
         """ Clears all internal buffers, throwing away all data. """
         """ Clears all internal buffers, throwing away all data. """
         self.transport.reset()
         self.transport.reset()
         self.com_port.flush()
         self.com_port.flush()
-        # time.sleep(0.015)
+
         self.com_port.reset_input_buffer()
         self.com_port.reset_input_buffer()
         self.com_port.reset_output_buffer()
         self.com_port.reset_output_buffer()
 
 
     def close_serial(self):
     def close_serial(self):
+        """ Closes the serial port """
         self.com_port.close()
         self.com_port.close()
 
 
     def set_baudrate(self, baud):
     def set_baudrate(self, baud):
+        """ Set the baudrate of the serial port """
         self.com_port.baudrate = baud
         self.com_port.baudrate = baud
         self.com_port.close()
         self.com_port.close()
         time.sleep(0.5)
         time.sleep(0.5)
@@ -89,8 +87,6 @@ class ShellmattaSerial():
                                              False,
                                              False,
                                              None)
                                              None)
 
 
-        # self.com_port._reconfigure_port
-
 # start interactive mode if not used as a module
 # start interactive mode if not used as a module
 if __name__ == "__main__":
 if __name__ == "__main__":
 
 

+ 76 - 39
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
@@ -24,9 +24,12 @@
 #include "shellmatta_escape.h"
 #include "shellmatta_escape.h"
 #include "shellmatta_opt.h"
 #include "shellmatta_opt.h"
 #include "shellmatta_ymodem.h"
 #include "shellmatta_ymodem.h"
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_TRANSPORT
 #include "shellmatta_transport.h"
 #include "shellmatta_transport.h"
 #endif
 #endif
+#ifdef SHELLMATTA_AUTHENTICATION
+#include "shellmatta_auth.h"
+#endif
 #include <stddef.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdint.h>
 #include <string.h>
 #include <string.h>
@@ -164,13 +167,13 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
             if(0u == inst->hereLength)
             if(0u == inst->hereLength)
             {
             {
                 /**
                 /**
-                    *  @dot
-                    *  digraph heredocParser {
-                    *      start -> wait [ label="<< in first line - store delimiter" ];
-                    *      wait -> wait [ label="delimiter not detected" ];
-                    *      wait -> end [ label="delimiter found - remove all heredoc stuff and send the input to the command" ];
-                    *  }
-                    *  @enddot */
+                     *  @dot
+                     *  digraph heredocParser {
+                     *      start -> wait [ label="<< in first line - store delimiter" ];
+                     *      wait -> wait [ label="delimiter not detected" ];
+                     *      wait -> end [ label="delimiter found - remove all heredoc stuff and send the input to the command" ];
+                     *  }
+                     *  @enddot */
 
 
                 /** -# check for heredoc - add string delimiter to stop strstr from searching too far */
                 /** -# check for heredoc - add string delimiter to stop strstr from searching too far */
                 inst->buffer[inst->inputCount] = '\0';
                 inst->buffer[inst->inputCount] = '\0';
@@ -296,14 +299,25 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
                     {
                     {
                         utils_writeEcho(inst, "\r\n", 2u);
                         utils_writeEcho(inst, "\r\n", 2u);
                         shellmatta_opt_init(inst, cmdLen + 1u);
                         shellmatta_opt_init(inst, cmdLen + 1u);
+
+#ifdef SHELLMATTA_AUTHENTICATION
+                        cmdRet = SHELLMATTA_OK;
+                        if (SHELLMATTA_OK == shellmatta_auth_is_cmd_permitted(inst, cmd))
+                        {
+                            cmdExecuted = 1u;
+                            cmdRet = cmd->cmdFct(handle, inst->buffer, inst->inputCount);
+                        }
+
+#else
                         cmdExecuted = 1u;
                         cmdExecuted = 1u;
                         cmdRet = cmd->cmdFct(handle, inst->buffer, inst->inputCount);
                         cmdRet = cmd->cmdFct(handle, inst->buffer, inst->inputCount);
+#endif
 
 
                         switch(cmdRet)
                         switch(cmdRet)
                         {
                         {
                             case SHELLMATTA_CONTINUE:
                             case SHELLMATTA_CONTINUE:
                                 /** -# initialize stdin buffer and continuous cmd */
                                 /** -# initialize stdin buffer and continuous cmd */
-                                inst->stdinIdx      = inst->inputCount + 1u;
+                                inst->stdinIdx      = inst->bufferSize - 2u;
                                 inst->stdinLength   = 0u;
                                 inst->stdinLength   = 0u;
                                 inst->continuousCmd = cmd;
                                 inst->continuousCmd = cmd;
                                 ret                 = cmdRet;
                                 ret                 = cmdRet;
@@ -328,9 +342,16 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
 
 
                 if ((0u == cmdExecuted) && (inst->inputCount > 0))
                 if ((0u == cmdExecuted) && (inst->inputCount > 0))
                 {
                 {
-                    SHELLMATTA_WRITE("\r\nCommand: ", 11u);
-                    SHELLMATTA_WRITE(inst->buffer, inst->inputCount);
-                    SHELLMATTA_WRITE(" not found", 10u);
+                    if (inst->echoEnabled)
+                    {
+                        inst->write("\r\nCommand: ", 11u);
+                        inst->write(inst->buffer, inst->inputCount);
+                        inst->write(" not found", 10u);
+                    }
+                    else
+                    {
+                        inst->write("\r\nCommand not found!", 20u);
+                    }
                 }
                 }
 
 
                 /** -# terminate this session if no continuous mode is requested */
                 /** -# terminate this session if no continuous mode is requested */
@@ -342,7 +363,7 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
             }
             }
         }
         }
         /** -# ignore newline as first character (to be compatible to
         /** -# ignore newline as first character (to be compatible to
-            * terminals sending newline after return) */
+         * terminals sending newline after return */
         else if((0u == inst->inputCount) && ('\n' == data[inst->byteCounter]))
         else if((0u == inst->inputCount) && ('\n' == data[inst->byteCounter]))
         {
         {
             /* do nothing */
             /* do nothing */
@@ -354,7 +375,7 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
             autocomplete_run(inst);
             autocomplete_run(inst);
         }
         }
         /** -# check for cancel -
         /** -# check for cancel -
-            *      terminate current input and print prompt again */
+         *      terminate current input and print prompt again */
         else if('\x03' == data[inst->byteCounter])
         else if('\x03' == data[inst->byteCounter])
         {
         {
             inst->dirty = false;
             inst->dirty = false;
@@ -438,54 +459,49 @@ 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;
         inst->ymodemState           = INACTIVE;
         inst->ymodemState           = INACTIVE;
         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;
         }
         }
 
 
         inst->magic = SHELLMATTA_MAGIC;
         inst->magic = SHELLMATTA_MAGIC;
         *handle     = (shellmatta_handle_t)inst;
         *handle     = (shellmatta_handle_t)inst;
 
 
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_TRANSPORT
         /* init transport layer */
         /* init transport layer */
-        inst->transportEnabled      = true;
+        inst->transportEnabled  = true;
         inst->transportBusyMark = 0u;
         inst->transportBusyMark = 0u;
         shellmatta_transport_init(&inst->transportLayer, inst->write);
         shellmatta_transport_init(&inst->transportLayer, inst->write);
 #endif
 #endif
@@ -507,7 +523,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;
@@ -537,6 +553,11 @@ shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle, bool pri
         inst->ymodemState           = INACTIVE;
         inst->ymodemState           = INACTIVE;
         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 */
@@ -628,6 +649,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
     {
     {
@@ -644,7 +681,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;
@@ -747,7 +784,7 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t     handle,
 {
 {
     shellmatta_retCode_t    ret   = SHELLMATTA_OK;
     shellmatta_retCode_t    ret   = SHELLMATTA_OK;
     shellmatta_instance_t   *inst = (shellmatta_instance_t*)handle;
     shellmatta_instance_t   *inst = (shellmatta_instance_t*)handle;
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_TRANSPORT
     char                    *tmpData;
     char                    *tmpData;
     uint32_t                tmpSize = 0;
     uint32_t                tmpSize = 0;
     uint32_t                i;
     uint32_t                i;
@@ -758,7 +795,7 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t     handle,
     if(     (NULL               != inst)
     if(     (NULL               != inst)
         &&  (SHELLMATTA_MAGIC   == inst->magic))
         &&  (SHELLMATTA_MAGIC   == inst->magic))
     {
     {
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_TRANSPORT
         if (inst->transportEnabled)
         if (inst->transportEnabled)
         {
         {
             for (i = inst->transportBusyMark; i < size; i ++)
             for (i = inst->transportBusyMark; i < size; i ++)
@@ -938,7 +975,7 @@ uint8_t shellmatta_ymodem(  shellmatta_handle_t             handle,
                             uint16_t*                       packetSize,
                             uint16_t*                       packetSize,
                             shellmatta_ymodem_callbacks_t   callbacks)
                             shellmatta_ymodem_callbacks_t   callbacks)
 {
 {
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_TRANSPORT
     /* disable transport layer so control symbols won't be caught by it */
     /* disable transport layer so control symbols won't be caught by it */
     ((shellmatta_instance_t*)handle)->transportEnabled = false;
     ((shellmatta_instance_t*)handle)->transportEnabled = false;
 #endif
 #endif

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

+ 39 - 21
src/shellmatta_transport.c

@@ -1,9 +1,17 @@
+/*
+ * 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    shellmatta_transport.c
  * @file    shellmatta_transport.c
  * @brief   transport layer functions of shellmatta
  * @brief   transport layer functions of shellmatta
  * @author  Simon Fischer <fischer.simon.1991@gmail.com>
  * @author  Simon Fischer <fischer.simon.1991@gmail.com>
  */
  */
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_TRANSPORT
 #include "shellmatta_transport.h"
 #include "shellmatta_transport.h"
 #include "shellmatta.h"
 #include "shellmatta.h"
 #include "shellmatta_crc.h"
 #include "shellmatta_crc.h"
@@ -38,13 +46,13 @@ static shellmatta_retCode_t shellmatta_transport_send(shellmatta_transport_layer
 
 
     crc = crc32Calc((char*) packet, SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength);
     crc = crc32Calc((char*) packet, SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength);
 
 
-    /* append crc to end of payload */
+    /** -# append crc to end of payload */
     rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength] = (uint8_t)(crc >> 24u);
     rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength] = (uint8_t)(crc >> 24u);
     rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength + 1u] = (uint8_t)(crc >> 16u);
     rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength + 1u] = (uint8_t)(crc >> 16u);
     rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength + 2u] = (uint8_t)(crc >> 8u);
     rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength + 2u] = (uint8_t)(crc >> 8u);
     rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength + 3u] = (uint8_t)(crc);
     rawPacket[SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength + 3u] = (uint8_t)(crc);
 
 
-    /* use original write function to send full buffer */
+    /** -# use original write function to send full buffer */
     return transportLayer->writeFct((char*) rawPacket,
     return transportLayer->writeFct((char*) rawPacket,
                                     SHELLMATTA_TRANSPORT_LENGTH_STATIC +
                                     SHELLMATTA_TRANSPORT_LENGTH_STATIC +
                                     header->payloadLength);
                                     header->payloadLength);
@@ -59,6 +67,7 @@ static shellmatta_retCode_t shellmatta_transport_send(shellmatta_transport_layer
 shellmatta_retCode_t shellmatta_transport_init( shellmatta_transport_layer_t    *transportLayer,
 shellmatta_retCode_t shellmatta_transport_init( shellmatta_transport_layer_t    *transportLayer,
                                                 shellmatta_write_t              writeFct)
                                                 shellmatta_write_t              writeFct)
 {
 {
+    /** -# clear instance and store write function */
     memset(transportLayer, 0u, sizeof(shellmatta_transport_layer_t));
     memset(transportLayer, 0u, sizeof(shellmatta_transport_layer_t));
     transportLayer->writeFct = writeFct;
     transportLayer->writeFct = writeFct;
 
 
@@ -119,13 +128,16 @@ shellmatta_retCode_t shellmatta_transport_reset(shellmatta_handle_t handle)
     if(     (NULL               != inst)
     if(     (NULL               != inst)
         &&  (SHELLMATTA_MAGIC   == inst->magic))
         &&  (SHELLMATTA_MAGIC   == inst->magic))
     {
     {
+        /** -# set transport layer back to initial state */
         shellmatta_transport_layer_t *transportLayer = &inst->transportLayer;
         shellmatta_transport_layer_t *transportLayer = &inst->transportLayer;
-        uint8_t sequenceH2S = transportLayer->sequenceH2S;
-        uint8_t sequenceS2H = transportLayer->sequenceS2H;
-        memset(transportLayer, 0u, sizeof(shellmatta_transport_layer_t));
 
 
-        transportLayer->sequenceH2S = sequenceH2S;
-        transportLayer->sequenceS2H = sequenceS2H;
+        transportLayer->state = SHELLMATTA_TRANSPORT_STATE_WAIT;
+        transportLayer->active = transportLayer->mandatory;
+        transportLayer->headerIndex = 0u;
+        transportLayer->payloadIndex = 0u;
+        transportLayer->crcIndex = 0u;
+        memset(&transportLayer->inPacket, 0, sizeof(transportLayer->inPacket));
+        memset(&transportLayer->outPacket, 0, sizeof(transportLayer->outPacket));
     }
     }
     else
     else
     {
     {
@@ -209,7 +221,7 @@ shellmatta_retCode_t shellmatta_transport_process(shellmatta_transport_layer_t
 
 
         break;
         break;
 
 
-    /* read payload for previously read length of payload */
+    /** -# read payload for previously read length of payload */
     case SHELLMATTA_TRANSPORT_STATE_GET_PAYLOAD:
     case SHELLMATTA_TRANSPORT_STATE_GET_PAYLOAD:
 
 
         transportLayer->inPacket.payload[transportLayer->payloadIndex] = byte;
         transportLayer->inPacket.payload[transportLayer->payloadIndex] = byte;
@@ -221,7 +233,7 @@ shellmatta_retCode_t shellmatta_transport_process(shellmatta_transport_layer_t
         }
         }
         break;
         break;
 
 
-    /* read crc32 for four bytes */
+    /** -# read crc32 for four bytes */
     case SHELLMATTA_TRANSPORT_STATE_GET_CRC:
     case SHELLMATTA_TRANSPORT_STATE_GET_CRC:
 
 
         transportLayer->crcIndex ++;
         transportLayer->crcIndex ++;
@@ -233,10 +245,9 @@ shellmatta_retCode_t shellmatta_transport_process(shellmatta_transport_layer_t
                                                    rawPacket,
                                                    rawPacket,
                                                    SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength);
                                                    SHELLMATTA_TRANSPORT_LENGTH_HEADER + header->payloadLength);
 
 
-            /* if crc is correct, further handling of data depends on type of packet */
+            /** -# if crc is correct, further handling of data depends on type of packet */
             if (transportLayer->inPacket.crc == refCrc)
             if (transportLayer->inPacket.crc == refCrc)
             {
             {
-
                 transportLayer->active = true;
                 transportLayer->active = true;
 
 
                 /* crc is correct */
                 /* crc is correct */
@@ -245,54 +256,60 @@ shellmatta_retCode_t shellmatta_transport_process(shellmatta_transport_layer_t
                 switch (header->packetType)
                 switch (header->packetType)
                 {
                 {
                 case SHELLMATTA_TRANSPORT_PACKET_DATA:
                 case SHELLMATTA_TRANSPORT_PACKET_DATA:
-                    /** return pointer to payload and length */
+                    /** -# return pointer to payload and length */
                     *data = (char*)&transportLayer->inPacket.payload;
                     *data = (char*)&transportLayer->inPacket.payload;
                     *length = header->payloadLength;
                     *length = header->payloadLength;
                     ret = SHELLMATTA_OK;
                     ret = SHELLMATTA_OK;
                     break;
                     break;
                 
                 
                 case SHELLMATTA_TRANSPORT_PACKET_SEQ_CNT_REQUEST:
                 case SHELLMATTA_TRANSPORT_PACKET_SEQ_CNT_REQUEST:
-                    /* send out packet with no payload */
+                    /** -# send out packet with no payload */
                     intPacket.header.packetType = SHELLMATTA_TRANSPORT_PACKET_SEQ_CNT_RESPOND;
                     intPacket.header.packetType = SHELLMATTA_TRANSPORT_PACKET_SEQ_CNT_RESPOND;
                     intPacket.header.payloadLength = 0u;
                     intPacket.header.payloadLength = 0u;
                     (void)shellmatta_transport_send(transportLayer, (shellmatta_transport_packet_t *)&intPacket);
                     (void)shellmatta_transport_send(transportLayer, (shellmatta_transport_packet_t *)&intPacket);
                     break;
                     break;
 
 
                 case SHELLMATTA_TRANSPORT_PACKET_SEQ_CNT_RESPOND:
                 case SHELLMATTA_TRANSPORT_PACKET_SEQ_CNT_RESPOND:
+                    /** -# ignore #SHELLMATTA_TRANSPORT_PACKET_SEQ_CNT_RESPOND - nothing to do */
                     break;
                     break;
                 
                 
                 case SHELLMATTA_TRANSPORT_PACKET_MAX_BUFFERSIZE_REQUEST:
                 case SHELLMATTA_TRANSPORT_PACKET_MAX_BUFFERSIZE_REQUEST:
+                    /** @todo implement */
                     break;
                     break;
 
 
                 case SHELLMATTA_TRANSPORT_PACKET_MAX_BUFFERSIZE_RESPOND:
                 case SHELLMATTA_TRANSPORT_PACKET_MAX_BUFFERSIZE_RESPOND:
+                    /** -# ignore #SHELLMATTA_TRANSPORT_PACKET_MAX_BUFFERSIZE_RESPOND - nothing to do */
                     break;
                     break;
                 
                 
                 case SHELLMATTA_TRANSPORT_PACKET_SEARCH_DEVICE_REQUEST:
                 case SHELLMATTA_TRANSPORT_PACKET_SEARCH_DEVICE_REQUEST:
+                    /** @todo implement */
                     break;
                     break;
                 
                 
                 case SHELLMATTA_TRANSPORT_PACKET_SEARCH_DEVICE_RESPOND:
                 case SHELLMATTA_TRANSPORT_PACKET_SEARCH_DEVICE_RESPOND:
+                    /** -# ignore #SHELLMATTA_TRANSPORT_PACKET_SEARCH_DEVICE_RESPOND - nothing to do */
                     break;
                     break;
 
 
                 case SHELLMATTA_TRANSPORT_PACKET_SET_ADDRESS_REQUEST:
                 case SHELLMATTA_TRANSPORT_PACKET_SET_ADDRESS_REQUEST:
+                    /** @todo implement */
                     break;
                     break;
 
 
                 case SHELLMATTA_TRANSPORT_PACKET_SET_ADDRESS_RESPOND:
                 case SHELLMATTA_TRANSPORT_PACKET_SET_ADDRESS_RESPOND:
+                    /** -# ignore #SHELLMATTA_TRANSPORT_PACKET_SET_ADDRESS_RESPOND - nothing to do */
                     break;
                     break;
 
 
-                /* wrong packet type */
                 default:
                 default:
-                    /* undo sequence counter increment */
+                    /** -# undo sequence counter increment on wrong packet type */
                     transportLayer->sequenceH2S--;
                     transportLayer->sequenceH2S--;
                     break;
                     break;
                 }
                 }
             }
             }
             else
             else
             {
             {
-                /* crc is incorrect */
+                /** -#  return error if crc is incorrect */
                 ret = SHELLMATTA_ERROR;
                 ret = SHELLMATTA_ERROR;
             }
             }
 
 
-            /* reset state machine */
+            /** -# reset state machine */
             transportLayer->state = SHELLMATTA_TRANSPORT_STATE_WAIT;
             transportLayer->state = SHELLMATTA_TRANSPORT_STATE_WAIT;
         }
         }
         break;
         break;
@@ -315,6 +332,7 @@ shellmatta_retCode_t shellmatta_transport_process(shellmatta_transport_layer_t
  * 
  * 
  * @param[in, out]  transportLayer  transport layer instance to work on
  * @param[in, out]  transportLayer  transport layer instance to work on
  * @param[in]       data            pointer to input data to process
  * @param[in]       data            pointer to input data to process
+ * @param[in]       length          length of data to process
  * @return          errorcode       #SHELLMATTA_OK
  * @return          errorcode       #SHELLMATTA_OK
  */
  */
 shellmatta_retCode_t shellmatta_transport_write(shellmatta_transport_layer_t *transportLayer,
 shellmatta_retCode_t shellmatta_transport_write(shellmatta_transport_layer_t *transportLayer,
@@ -326,13 +344,13 @@ shellmatta_retCode_t shellmatta_transport_write(shellmatta_transport_layer_t *tr
     shellmatta_transport_packet_t *packet = &transportLayer->outPacket;
     shellmatta_transport_packet_t *packet = &transportLayer->outPacket;
     shellmatta_transport_header_t *header = &transportLayer->outPacket.header;
     shellmatta_transport_header_t *header = &transportLayer->outPacket.header;
 
 
-    /* handle data with length bigger than max length */
-    uint32_t processedLength    = 0u;
+    /** -# handle data with length bigger than max length */
+    uint32_t processedLength = 0u;
     uint32_t piledUpPayload;
     uint32_t piledUpPayload;
     uint32_t splitLength;
     uint32_t splitLength;
     uint32_t restOfPayload;
     uint32_t restOfPayload;
 
 
-    /* foot-controlled loop to send packets without payload length */
+    /** -# foot-controlled loop to send packets without payload length */
     do
     do
     {
     {
         piledUpPayload = header->payloadLength;
         piledUpPayload = header->payloadLength;

+ 22 - 11
src/shellmatta_transport.h

@@ -1,3 +1,11 @@
+/*
+ * 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    shellmatta_transport.h
  * @file    shellmatta_transport.h
  * @brief   transport layer functions of shellmatta
  * @brief   transport layer functions of shellmatta
@@ -9,14 +17,6 @@
 
 
 #include "shellmatta.h"
 #include "shellmatta.h"
 
 
-/** @brief packet definition with reduced payload (for internal packets) */
-typedef struct __attribute__((__packed__))
-{
-    shellmatta_transport_header_t   header; /**< header of the packet   */
-    //char payload[SHELLMATTA_TRANPORT_PAYLOAD_MAXLENGTH]; TODO - this has to be the maximum needed internal payload
-    uint32_t                        crc;    /**< checksum of the packet */
-} shellmatta_transport_packet_int_t;
-
 /** @brief value of start-of-header character */
 /** @brief value of start-of-header character */
 #define SHELLMATTA_TRANSPORT_START_OF_HEADER    0x01u
 #define SHELLMATTA_TRANSPORT_START_OF_HEADER    0x01u
 /** @brief currently supported protocol version */
 /** @brief currently supported protocol version */
@@ -24,17 +24,28 @@ typedef struct __attribute__((__packed__))
 
 
 /* header field length defines */
 /* header field length defines */
 /** @brief length of header */
 /** @brief length of header */
-#define SHELLMATTA_TRANSPORT_LENGTH_HEADER  ((uint8_t)(8))
+#define SHELLMATTA_TRANSPORT_LENGTH_HEADER      ((uint8_t)(8))
+/** @brief maximum length of shellmatta internal packages (e.g. sequence counter packets) */
+#define SHELLMATTA_TRANSPORT_LENGTH_PAYLOAD_INT ((uint8_t)(32))
 /** @brief length of crc32 */
 /** @brief length of crc32 */
-#define SHELLMATTA_TRANSPORT_LENGTH_CRC     ((uint8_t)(4))
+#define SHELLMATTA_TRANSPORT_LENGTH_CRC         ((uint8_t)(4))
 /** @brief length of crc32 */
 /** @brief length of crc32 */
-#define SHELLMATTA_TRANSPORT_LENGTH_STATIC  (SHELLMATTA_TRANSPORT_LENGTH_HEADER + SHELLMATTA_TRANSPORT_LENGTH_CRC)
+#define SHELLMATTA_TRANSPORT_LENGTH_STATIC      (SHELLMATTA_TRANSPORT_LENGTH_HEADER + SHELLMATTA_TRANSPORT_LENGTH_CRC)
 
 
 /** @brief helper macro for CRC calculation */
 /** @brief helper macro for CRC calculation */
 #define SHELLMATTA_TRANSPORT_CALC_CRC(transporLayer, data, size)    NULL != (transportLayer)->customCrcFct ?         \
 #define SHELLMATTA_TRANSPORT_CALC_CRC(transporLayer, data, size)    NULL != (transportLayer)->customCrcFct ?         \
                                                                     (transportLayer)->customCrcFct((data), (size)) : \
                                                                     (transportLayer)->customCrcFct((data), (size)) : \
                                                                     crc32Calc((data), (size));
                                                                     crc32Calc((data), (size));
 
 
+/** @brief packet definition with reduced payload (for internal packets) */
+typedef struct __attribute__((__packed__))
+{
+    shellmatta_transport_header_t   header;                 /**< header of the packet   */
+    char payload[SHELLMATTA_TRANSPORT_LENGTH_PAYLOAD_INT];  /**< payload of the packet  */
+    uint32_t                        crc;                    /**< checksum of the packet */
+} shellmatta_transport_packet_int_t;
+
+
 /**
 /**
  * @brief definitions of shellmatta transport layer packet types
  * @brief definitions of shellmatta transport layer packet types
  */
  */

+ 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>
 
 
 /**
 /**
@@ -203,7 +206,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;
@@ -228,6 +231,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 */
@@ -424,6 +428,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)
             {
             {
@@ -436,6 +447,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;
@@ -475,7 +493,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
@@ -492,6 +514,14 @@ void utils_terminateInput(shellmatta_instance_t *inst)
     inst->continuousCmd     = NULL;
     inst->continuousCmd     = NULL;
     inst->busyCmd           = NULL;
     inst->busyCmd           = NULL;
     SHELLMATTA_WRITE("\r\n", 2u);
     SHELLMATTA_WRITE("\r\n", 2u);
+#ifdef SHELLMATTA_AUTHENTICATION
+    inst->loginState        = SHELLMATTA_AUTH_IDLE;
+    if (NULL != inst->userPointer)
+    {
+        SHELLMATTA_WRITE(inst->userPointer->username, strlen(inst->userPointer->username));
+        SHELLMATTA_WRITE("@", 1);
+    }
+#endif
     SHELLMATTA_WRITE(inst->prompt, strlen(inst->prompt));
     SHELLMATTA_WRITE(inst->prompt, strlen(inst->prompt));
 }
 }
 
 

+ 3 - 3
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
@@ -19,7 +19,7 @@
 #ifndef _SHELLMATTA_UTILS_H_
 #ifndef _SHELLMATTA_UTILS_H_
 #define _SHELLMATTA_UTILS_H_
 #define _SHELLMATTA_UTILS_H_
 
 
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_TRANSPORT
 #include "shellmatta_transport.h"
 #include "shellmatta_transport.h"
 #endif
 #endif
 #include "shellmatta.h"
 #include "shellmatta.h"
@@ -130,7 +130,7 @@ void utils_forwardCursor(           shellmatta_instance_t *inst,
                                     uint32_t length);
                                     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,

+ 5 - 4
src/shellmatta_ymodem.c

@@ -7,6 +7,7 @@
 #include "shellmatta_ymodem.h"
 #include "shellmatta_ymodem.h"
 #include "shellmatta_crc.h"
 #include "shellmatta_crc.h"
 #include "shellmatta_utils.h"
 #include "shellmatta_utils.h"
+#include <stddef.h>
 
 
 /* current ymodem packet size */
 /* current ymodem packet size */
 uint16_t    yModemPacketSize = YMODEM_PACKET_SIZE;
 uint16_t    yModemPacketSize = YMODEM_PACKET_SIZE;
@@ -443,7 +444,7 @@ void shellmatta_ymodem_check_packet(shellmatta_handle_t handle)
 
 
     case YMODEM_BODY:
     case YMODEM_BODY:
         /* call the ymodem receive packet callback */
         /* call the ymodem receive packet callback */
-        if (shellmatta_ymodem_callbacks.yModemRecvPacketCallback != (void*)0u)
+        if (NULL != shellmatta_ymodem_callbacks.yModemRecvPacketCallback)
         {
         {
             shellmatta_ymodem_callbacks.yModemRecvPacketCallback();
             shellmatta_ymodem_callbacks.yModemRecvPacketCallback();
         }
         }
@@ -451,7 +452,7 @@ void shellmatta_ymodem_check_packet(shellmatta_handle_t handle)
 
 
     case YMODEM_FOOTER:
     case YMODEM_FOOTER:
         /* call the ymodem transmission complete callback */
         /* call the ymodem transmission complete callback */
-        if (shellmatta_ymodem_callbacks.ymodemTransmissionCompleteCallback != (void*)0u)
+        if (NULL != shellmatta_ymodem_callbacks.ymodemTransmissionCompleteCallback)
         {
         {
             shellmatta_ymodem_callbacks.ymodemTransmissionCompleteCallback();
             shellmatta_ymodem_callbacks.ymodemTransmissionCompleteCallback();
         }
         }
@@ -528,7 +529,7 @@ void shellmatta_ymodem_reset(shellmatta_handle_t handle, bool doCancel)
     {
     {
         /* send cancel symbol */
         /* send cancel symbol */
         shellmatta_ymodem_control(handle, YMODEM_CA);
         shellmatta_ymodem_control(handle, YMODEM_CA);
-        if (shellmatta_ymodem_callbacks.yModemCancelCallback != (void*)0u)
+        if (NULL != shellmatta_ymodem_callbacks.yModemCancelCallback)
         {
         {
             shellmatta_ymodem_callbacks.yModemCancelCallback();
             shellmatta_ymodem_callbacks.yModemCancelCallback();
         }
         }
@@ -553,7 +554,7 @@ void shellmatta_ymodem_reset(shellmatta_handle_t handle, bool doCancel)
 
 
     shellmatta_ymodem_current_data_type = YMODEM_NONE;
     shellmatta_ymodem_current_data_type = YMODEM_NONE;
 
 
-#ifdef SHELLMATTA_TRANSPORT_ENABLE
+#ifdef SHELLMATTA_TRANSPORT
     /* re-enable transport layer */
     /* re-enable transport layer */
     ((shellmatta_instance_t*)handle)->transportEnabled = true;
     ((shellmatta_instance_t*)handle)->transportEnabled = true;
 #endif
 #endif

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


+ 31 - 38
test/integrationtest/test_integration.cpp

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

+ 3 - 3
test/integrationtest/test_integration_busy.cpp

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

+ 5 - 5
test/integrationtest/test_integration_continue.cpp

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

+ 9 - 9
test/integrationtest/test_integration_help.cpp

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

+ 1 - 1
test/integrationtest/test_integration_history.cpp

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

+ 3 - 3
test/integrationtest/test_integration_opt.cpp

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

+ 3 - 3
test/integrationtest/test_integration_optLong.cpp

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

+ 847 - 0
test/integrationtest_auth/test_integration_auth.cpp

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

+ 11 - 0
test/integrationtest_auth/test_main.cpp

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

+ 126 - 15
test/integrationtest/test_integration_transport.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
@@ -147,8 +147,8 @@ SCENARIO("Integration test of Transport layer", "[integration, transport]")
             {
             {
                 char *dummyData =   (char*)"crc error\r\n\r\nshellmatta->";
                 char *dummyData =   (char*)"crc error\r\n\r\nshellmatta->";
 
 
-                CHECK( write_length == strlen(dummyData));
-                REQUIRE( strcmp(dummyData, write_data) == 0);
+                CHECK(write_length == strlen(dummyData));
+                REQUIRE(strcmp(dummyData, write_data) == 0);
             }
             }
         }
         }
 
 
@@ -167,8 +167,8 @@ SCENARIO("Integration test of Transport layer", "[integration, transport]")
                                            "shellmatta->"
                                            "shellmatta->"
                                            "\xc8\xae\xc0\x56";
                                            "\xc8\xae\xc0\x56";
 
 
-                CHECK( write_length == 59u);
-                REQUIRE( memcmp(write_data, dummyData, 59) == 0);
+                CHECK(write_length == 59u);
+                REQUIRE(memcmp(write_data, dummyData, 59) == 0);
             }
             }
         }
         }
 
 
@@ -200,8 +200,8 @@ SCENARIO("Integration test of Transport layer", "[integration, transport]")
                                            "shellmatta->"
                                            "shellmatta->"
                                            "\x00\xc9\x5d\x37";
                                            "\x00\xc9\x5d\x37";
 
 
-                CHECK( write_length == 406u);
-                REQUIRE( memcmp(write_data, dummyData, 406u) == 0);
+                CHECK(write_length == 406u);
+                REQUIRE(memcmp(write_data, dummyData, 406u) == 0);
             }
             }
         }
         }
 
 
@@ -233,8 +233,8 @@ SCENARIO("Integration test of Transport layer", "[integration, transport]")
                                            "shellmatta->"
                                            "shellmatta->"
                                            "\x00\xc9\x5d\x37";
                                            "\x00\xc9\x5d\x37";
 
 
-                CHECK( write_length == 406u);
-                REQUIRE( memcmp(write_data, dummyData, 406u) == 0);
+                CHECK(write_length == 406u);
+                REQUIRE(memcmp(write_data, dummyData, 406u) == 0);
             }
             }
         }
         }
 
 
@@ -256,9 +256,8 @@ SCENARIO("Integration test of Transport layer", "[integration, transport]")
                                            "doSomething argument - length: 20\r\n"
                                            "doSomething argument - length: 20\r\n"
                                            "shellmatta->";
                                            "shellmatta->";
 
 
-                CHECK( write_length == 106);
-                REQUIRE( memcmp(write_data, dummyData, 106) == 0);
-
+                CHECK(write_length == 106);
+                REQUIRE(memcmp(write_data, dummyData, 106) == 0);
             }
             }
         }
         }
 
 
@@ -273,7 +272,7 @@ SCENARIO("Integration test of Transport layer", "[integration, transport]")
 
 
             THEN("The Shellmatta does not respond")
             THEN("The Shellmatta does not respond")
             {
             {
-                CHECK( write_length == 0u);
+                CHECK(write_length == 0u);
 
 
                 AND_WHEN("The flush method is called")
                 AND_WHEN("The flush method is called")
                 {
                 {
@@ -287,8 +286,8 @@ SCENARIO("Integration test of Transport layer", "[integration, transport]")
                                                 "shellmatta->"
                                                 "shellmatta->"
                                                 "\xc8\xae\xc0\x56";
                                                 "\xc8\xae\xc0\x56";
 
 
-                        CHECK( write_length == 59u);
-                        REQUIRE( memcmp(write_data, dummyData, 59) == 0);
+                        CHECK(write_length == 59u);
+                        REQUIRE(memcmp(write_data, dummyData, 59) == 0);
                     }
                     }
                 }
                 }
             }
             }
@@ -327,3 +326,115 @@ SCENARIO("Integration test of Transport layer", "[integration, transport]")
         }
         }
     }
     }
 }
 }
+
+SCENARIO("Integration test of Transport layer with mandatory transport layer", "[integration, transport]")
+{
+    GIVEN("Shellmatta up and running with one command")
+    {
+        shellmatta_instance_t inst;
+        shellmatta_handle_t handle;
+        shellmatta_retCode_t ret;
+        char buffer[1024];
+        char historyBuffer[1024];
+
+        shellmatta_doInit(  &inst,
+                            &handle,
+                            buffer,
+                            sizeof(buffer),
+                            historyBuffer,
+                            sizeof(historyBuffer),
+                            "shellmatta->",
+                            NULL,
+                            writeFct);
+        shellmatta_addCmd(handle, &doSomethingCmd);
+
+        write_callCnt = 0u;
+        memset(write_data, 0, sizeof(write_data));
+        write_length = 0u;
+
+        WHEN("The tansport layer is set to mandatory") {
+            ret = shellmatta_transport_configure(handle, true, false, NULL);
+            CHECK(ret == SHELLMATTA_OK);
+
+            AND_WHEN("manual mode is used after transport layer mode")
+            {
+                /* check with valid payload - disable echo to reduce cluttering */
+                shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');
+                shellmatta_processData(handle, (char*)"\x01\x01\x00\x16\x00\x00\x00\x00"
+                                                    "doSomething argument\r\n"
+                                                    "\x7b\x49\xfa\x72", 34u);
+                shellmatta_processData(handle, (char*)"doSomething argument\r\n", 22u);
+                THEN("The shellmatta responds only to the first command - and waits for a new telegram to start")
+                {
+                    char *dummyData =   (char*)"\x01\x01\x00\x2f\x00\x00\x01\x01"
+                                            "doSomething argument - length: 20"
+                                            "\r\n"
+                                            "shellmatta->"
+                                            "\xc8\xae\xc0\x56";
+
+                    CHECK(write_length == 59);
+                    CHECK(memcmp(write_data, dummyData, write_length) == 0);
+                    REQUIRE(inst.transportLayer.state == SHELLMATTA_TRANSPORT_STATE_WAIT);
+                }
+            }
+        }
+    }
+}
+
+SCENARIO("Integration test of Transport layer - reset transport layer", "[integration, transport]")
+{
+    GIVEN("Shellmatta up and running with one command")
+    {
+        shellmatta_instance_t inst;
+        shellmatta_handle_t handle;
+        shellmatta_retCode_t ret;
+        char buffer[1024];
+        char historyBuffer[1024];
+
+        shellmatta_doInit(  &inst,
+                            &handle,
+                            buffer,
+                            sizeof(buffer),
+                            historyBuffer,
+                            sizeof(historyBuffer),
+                            "shellmatta->",
+                            NULL,
+                            writeFct);
+        shellmatta_addCmd(handle, &doSomethingCmd);
+
+        write_callCnt = 0u;
+        memset(write_data, 0, sizeof(write_data));
+        write_length = 0u;
+
+        WHEN("The tansport layer is set to mandatory") {
+            ret = shellmatta_transport_configure(handle, true, false, NULL);
+            CHECK(ret == SHELLMATTA_OK);
+
+            AND_WHEN("A partial telegram is passed")
+            {
+                /* check with valid payload - disable echo to reduce cluttering */
+                shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');
+                shellmatta_processData(handle, (char*)"\x01\x01\x00\x16\x00\x00\x00\x00"
+                                                    "doSomething argument", 28u);
+                THEN("The transport layer is in state SHELLMATTA_TRANSPORT_STATE_GET_PAYLOAD")
+                {
+                    CHECK(write_length == 0);
+                    CHECK_THAT(inst.transportLayer.inPacket.payload, Catch::Matchers::Equals("doSomething argument"));
+                    REQUIRE(inst.transportLayer.state == SHELLMATTA_TRANSPORT_STATE_GET_PAYLOAD);
+
+                    AND_WHEN("The transport layer is resetted")
+                    {
+                        ret = shellmatta_transport_reset(handle);
+                        CHECK(ret == SHELLMATTA_OK);
+
+                        THEN("The transport layer is in state wait again and no payload is set") {
+                            CHECK(write_length == 0);
+                            CHECK(inst.transportLayer.inPacket.payload[0] == '\0');
+                            REQUIRE(inst.transportLayer.state == SHELLMATTA_TRANSPORT_STATE_WAIT);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 11 - 0
test/integrationtest_transport/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
+}
+

+ 9 - 1
test/unittest/shellmatta_crc/test_crc32Fast.cpp

@@ -2,11 +2,19 @@
 #include <string.h>
 #include <string.h>
 
 
 #undef SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
 #undef SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
+#include "test_crc32_data.h"
 #include "src/shellmatta_crc.c"
 #include "src/shellmatta_crc.c"
 
 
 TEST_CASE( "shellmatta_crc crc32Fast" ) {
 TEST_CASE( "shellmatta_crc crc32Fast" ) {
 
 
     uint32_t crc = crc32Fast((char*)"123456789", 9, crc32Table);
     uint32_t crc = crc32Fast((char*)"123456789", 9, crc32Table);
 
 
-    REQUIRE( crc == 0xCBF43926u);
+    REQUIRE(crc == 0xCBF43926u);
+}
+
+TEST_CASE( "shellmatta_crc crc32Fast - more data" ) {
+
+    uint32_t crc = crc32Fast((char*)data, sizeof(data), crc32Table);
+
+    REQUIRE(crc == data_crc);
 }
 }

+ 8 - 0
test/unittest/shellmatta_crc/test_crc32Slow.cpp

@@ -2,6 +2,7 @@
 #include <string.h>
 #include <string.h>
 
 
 #define SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
 #define SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
+#include "test_crc32_data.h"
 #include "src/shellmatta_crc.c"
 #include "src/shellmatta_crc.c"
 
 
 TEST_CASE( "shellmatta_crc crc32Slow" ) {
 TEST_CASE( "shellmatta_crc crc32Slow" ) {
@@ -10,3 +11,10 @@ TEST_CASE( "shellmatta_crc crc32Slow" ) {
 
 
     REQUIRE( crc == 0xCBF43926u);
     REQUIRE( crc == 0xCBF43926u);
 }
 }
+
+TEST_CASE( "shellmatta_crc crc32Slow - more data" ) {
+
+    uint32_t crc = crc32Slow((char*)data, sizeof(data));
+
+    REQUIRE(crc == data_crc);
+}

+ 265 - 0
test/unittest/shellmatta_crc/test_crc32_data.h

@@ -0,0 +1,265 @@
+#ifndef _TEST_CRC32_DATA_H_
+#define _TEST_CRC32_DATA_H_
+
+const uint8_t data[] = {
+    0xB8, 0xCA, 0xFD, 0x92, 0x55, 0x6A, 0x2F, 0x22, 0x5B, 0xB3, 0x0F, 0xEE, 0x69, 0x4E, 0x83, 0x4A,
+    0x2A, 0x4E, 0x5E, 0x29, 0x58, 0x2C, 0xE0, 0x21, 0x1C, 0x92, 0x76, 0x7C, 0x87, 0xB1, 0x00, 0x18,
+    0x25, 0xFE, 0x49, 0x24, 0xDC, 0x8F, 0x82, 0x09, 0x9D, 0x83, 0xE6, 0x01, 0xA3, 0x12, 0xDF, 0x39,
+    0x2E, 0x4A, 0x3F, 0x75, 0x0D, 0x8D, 0x7E, 0x9D, 0x5B, 0x48, 0x01, 0x9C, 0x3D, 0x92, 0xD5, 0x81,
+    0x93, 0x97, 0x7C, 0xD1, 0xDE, 0xFD, 0x97, 0x6D, 0xD3, 0xB1, 0x9D, 0x84, 0x55, 0xCB, 0xF1, 0x69,
+    0xDC, 0x6B, 0xF1, 0x5D, 0x78, 0xC6, 0xDE, 0x87, 0xF5, 0xC9, 0xBE, 0xB8, 0xDA, 0xFE, 0x83, 0xC0,
+    0x3D, 0xA0, 0x2B, 0x61, 0xA9, 0x10, 0x9F, 0x25, 0x92, 0x06, 0x7A, 0xB2, 0x17, 0x43, 0x0E, 0x59,
+    0x02, 0x95, 0x4B, 0xF4, 0x56, 0x70, 0x54, 0x60, 0xCF, 0x79, 0xF5, 0x16, 0x26, 0xBA, 0x3D, 0xBC,
+    0x03, 0x59, 0xEF, 0xB1, 0xEC, 0x1A, 0x90, 0xDC, 0x93, 0xFE, 0x46, 0x5F, 0x60, 0xB8, 0xCC, 0xD8,
+    0x12, 0xE0, 0x27, 0x61, 0xCA, 0x13, 0xF6, 0x7D, 0xF5, 0x6D, 0x72, 0x93, 0xCC, 0xFD, 0x7D, 0xAF,
+    0x69, 0x32, 0x64, 0xB1, 0xBA, 0x60, 0x23, 0x12, 0xB2, 0xC8, 0x50, 0xEF, 0x8E, 0xDC, 0x05, 0x0A,
+    0x20, 0x99, 0x65, 0xDE, 0x5A, 0x31, 0xA1, 0x08, 0x96, 0x6E, 0x85, 0x9E, 0x5A, 0x74, 0xFE, 0x27,
+    0x97, 0xD3, 0x2C, 0x67, 0x8E, 0x1A, 0xD6, 0x1A, 0xF0, 0x48, 0x6A, 0x5E, 0xE2, 0xD6, 0xD7, 0x69,
+    0xE8, 0x41, 0xE8, 0xBA, 0xF0, 0x3C, 0xF4, 0xFF, 0x03, 0xFA, 0x02, 0x3B, 0x45, 0x3E, 0xC2, 0x07,
+    0x58, 0x17, 0xEB, 0xEA, 0x42, 0x77, 0xE9, 0x1B, 0x72, 0x15, 0xE8, 0x38, 0x82, 0x3F, 0xA5, 0xC0,
+    0xC6, 0x8C, 0x96, 0x59, 0xDA, 0x9A, 0x51, 0x32, 0xB4, 0x43, 0x41, 0x02, 0xE4, 0xF1, 0x0C, 0x85,
+    0x1B, 0x0C, 0x4B, 0x6A, 0x17, 0x96, 0x63, 0x11, 0x80, 0x7A, 0xA7, 0x9F, 0x76, 0x25, 0x16, 0x2F,
+    0xBC, 0x63, 0x5B, 0x31, 0xCB, 0xA8, 0xE2, 0x46, 0x43, 0x2D, 0x1E, 0x1F, 0x71, 0x93, 0x66, 0x2B,
+    0xF6, 0xF4, 0xF9, 0x25, 0xB1, 0x8E, 0x0D, 0xCB, 0x88, 0x78, 0x02, 0x4A, 0xAC, 0x0B, 0x13, 0x2B,
+    0x71, 0xE3, 0x26, 0xCC, 0xD9, 0xB4, 0x91, 0xB6, 0x6F, 0x54, 0xF9, 0x52, 0x0D, 0xA2, 0x9B, 0xD7,
+    0x9F, 0x47, 0xA5, 0x71, 0x19, 0x67, 0x74, 0xEC, 0x1A, 0xC2, 0xDD, 0x82, 0xF6, 0xE6, 0xCC, 0x7D,
+    0x2D, 0x5B, 0xE9, 0xCB, 0x7E, 0x01, 0x0D, 0xCE, 0x1B, 0x03, 0xB5, 0xEF, 0xB9, 0x0D, 0xBC, 0xBD,
+    0x72, 0xAE, 0x04, 0xB6, 0xBA, 0x1E, 0xEB, 0xEC, 0xEA, 0xC0, 0x9E, 0x25, 0x07, 0x22, 0xB3, 0x40,
+    0xDE, 0x51, 0x9B, 0xDF, 0x98, 0xC7, 0xCA, 0xB0, 0x4E, 0x3E, 0xBC, 0xDC, 0x5E, 0x38, 0x1E, 0x62,
+    0x6E, 0x09, 0xD0, 0x72, 0x65, 0xA5, 0x85, 0x16, 0xD1, 0x8D, 0x2E, 0xA5, 0x7A, 0x9B, 0x7E, 0xE5,
+    0x17, 0x7E, 0x38, 0xCF, 0x69, 0x30, 0x00, 0x52, 0x32, 0xB9, 0xF8, 0x98, 0xC7, 0xFB, 0x59, 0xA1,
+    0x39, 0x6A, 0xC6, 0x36, 0x4E, 0xE0, 0x1E, 0x89, 0xCD, 0xF9, 0xFB, 0x07, 0xCC, 0xA3, 0x29, 0x31,
+    0x0D, 0xCD, 0xBE, 0x77, 0x98, 0x5B, 0xAB, 0x7C, 0x16, 0xDE, 0xDA, 0x30, 0xA2, 0xA3, 0x4A, 0xA7,
+    0x18, 0x18, 0xA5, 0xA5, 0x0E, 0xA8, 0x51, 0x39, 0xFE, 0x87, 0xF7, 0xE6, 0x5E, 0x02, 0xF0, 0x3B,
+    0x99, 0x60, 0x31, 0xC5, 0x2F, 0x5C, 0x88, 0xCB, 0x6C, 0xCB, 0x56, 0x49, 0x83, 0xF0, 0x11, 0xF8,
+    0xF9, 0x8D, 0x35, 0x7A, 0xA0, 0xCC, 0x82, 0xED, 0xA8, 0x6F, 0x98, 0x70, 0x75, 0xF1, 0x57, 0x70,
+    0x3A, 0x8A, 0x97, 0xBC, 0x9C, 0x3A, 0x1D, 0xB2, 0xCC, 0x52, 0xE4, 0x1B, 0xE2, 0x15, 0x12, 0x6A,
+    0x6A, 0x77, 0x3C, 0x81, 0x64, 0x0B, 0xD5, 0x40, 0x34, 0x9D, 0xD9, 0x66, 0x3A, 0x1F, 0x24, 0x91,
+    0x11, 0xD5, 0xFB, 0x72, 0xB1, 0xF0, 0xB0, 0x76, 0xF0, 0xF7, 0x7F, 0x72, 0x19, 0xBB, 0xF6, 0x26,
+    0xA0, 0xBA, 0x89, 0x9A, 0x20, 0x1B, 0x33, 0xA2, 0x30, 0xB0, 0x37, 0x1D, 0xBB, 0xAC, 0x62, 0xB1,
+    0xE4, 0xFE, 0x6C, 0x13, 0xA5, 0x6D, 0x4D, 0x30, 0xB7, 0xF4, 0xA8, 0xAD, 0x68, 0xFD, 0xA9, 0xAC,
+    0x72, 0x6C, 0xEC, 0xBB, 0xFB, 0xA5, 0x4B, 0x56, 0x4B, 0xFB, 0xB4, 0x81, 0xE9, 0x30, 0x60, 0x39,
+    0x1C, 0xF4, 0xFE, 0xDE, 0x12, 0x94, 0xC4, 0xCC, 0x24, 0x38, 0x63, 0xC1, 0xF2, 0x6D, 0x5D, 0xCE,
+    0x5C, 0xD8, 0x3A, 0xEC, 0x81, 0x47, 0x8D, 0x74, 0x5B, 0x88, 0xD6, 0x0E, 0x99, 0xB4, 0xAE, 0xE6,
+    0xC7, 0xDD, 0xC7, 0x25, 0xF5, 0x3C, 0xA7, 0x0E, 0x5C, 0x63, 0x37, 0x35, 0xBE, 0x0E, 0x85, 0xB2,
+    0x7D, 0x7C, 0x4D, 0x4B, 0xA2, 0x90, 0x2F, 0xE6, 0x57, 0x10, 0xA7, 0xD7, 0x84, 0xB7, 0x25, 0xC8,
+    0x97, 0x11, 0xE2, 0x50, 0xB2, 0x30, 0x4F, 0x89, 0xAC, 0xCC, 0x31, 0x24, 0xB8, 0x57, 0xD9, 0xD2,
+    0x9A, 0x0A, 0x00, 0x08, 0xB4, 0x07, 0x2B, 0x6D, 0x5E, 0x03, 0xB5, 0x82, 0x47, 0x2B, 0xDF, 0x42,
+    0xE2, 0x1C, 0x6F, 0xD8, 0x0E, 0x2F, 0xD6, 0xA8, 0x82, 0x7B, 0xE0, 0x40, 0xAE, 0x36, 0x57, 0xFD,
+    0x19, 0x6A, 0x36, 0x67, 0x6D, 0x24, 0x3E, 0x9B, 0xAF, 0x86, 0x13, 0x48, 0x64, 0x75, 0x39, 0x0E,
+    0xA2, 0xBF, 0x8F, 0x4D, 0x34, 0xED, 0x1D, 0xA7, 0x6F, 0x2F, 0x59, 0xCB, 0x53, 0x0B, 0x3F, 0x55,
+    0x08, 0xB6, 0xD3, 0xC3, 0xEB, 0x54, 0xE9, 0xD8, 0xAF, 0x6F, 0x55, 0xF7, 0x41, 0x71, 0xD7, 0x38,
+    0x73, 0xEF, 0x6B, 0x52, 0xB2, 0x11, 0xC6, 0x98, 0x2D, 0x59, 0x33, 0x9E, 0x40, 0xA8, 0x15, 0x51,
+    0x15, 0x3D, 0xC0, 0x87, 0xAD, 0xF9, 0x73, 0x5F, 0xE8, 0x4B, 0x96, 0xF0, 0x25, 0x6B, 0x9F, 0x20,
+    0x98, 0xD5, 0x2D, 0x9E, 0x7A, 0x34, 0x3A, 0x61, 0x94, 0x1F, 0x8A, 0x25, 0xEF, 0x58, 0xA2, 0xBA,
+    0x94, 0x81, 0xEA, 0x34, 0x98, 0x68, 0xE4, 0x3F, 0x05, 0x59, 0x74, 0x2B, 0x3F, 0x26, 0xBC, 0x7A,
+    0xF7, 0xCD, 0x03, 0xFB, 0xE2, 0xE8, 0xA3, 0xBA, 0xA3, 0x5A, 0xFF, 0x5E, 0xC0, 0xD4, 0xF1, 0xB0,
+    0x7D, 0x38, 0x42, 0x60, 0xF4, 0xE9, 0x09, 0x5D, 0xD8, 0x8C, 0x11, 0x31, 0x9F, 0xD8, 0x9B, 0x4F,
+    0x19, 0x67, 0x22, 0x48, 0xA4, 0xAE, 0xF2, 0x31, 0x7E, 0x97, 0xB6, 0xDF, 0xF5, 0x4F, 0x55, 0xA4,
+    0x6B, 0x4E, 0xBD, 0xB4, 0x6D, 0xBA, 0x75, 0x6E, 0x54, 0x8D, 0x13, 0x1E, 0x3B, 0x2C, 0xF0, 0xFC,
+    0x2C, 0x68, 0xC0, 0x78, 0xDF, 0xFF, 0xD9, 0x28, 0x6B, 0x19, 0x55, 0xCE, 0xB6, 0x6B, 0x60, 0x5C,
+    0x9F, 0xE1, 0x54, 0xEB, 0x13, 0x0D, 0x7E, 0xFF, 0x94, 0xB6, 0xA3, 0xA7, 0xEC, 0x3F, 0xB0, 0x2F,
+    0x02, 0xC9, 0x17, 0x92, 0x16, 0x44, 0xD0, 0xD3, 0xD5, 0xD5, 0x0A, 0xE9, 0x0F, 0x41, 0xED, 0xF4,
+    0xFC, 0x43, 0x04, 0xD7, 0x5B, 0x05, 0x3B, 0x6F, 0xD3, 0x17, 0x71, 0x0F, 0x6F, 0xA3, 0x17, 0xEF,
+    0x10, 0xB6, 0x68, 0xB1, 0x2D, 0xDD, 0x13, 0x3C, 0x4A, 0x75, 0x85, 0x7E, 0xED, 0x5D, 0x16, 0xDA,
+    0x0A, 0xFB, 0xCE, 0x5C, 0x1C, 0xBC, 0x8B, 0xF1, 0x73, 0x74, 0xAD, 0x32, 0x66, 0x5F, 0xA3, 0x93,
+    0x71, 0x90, 0xF4, 0x04, 0x70, 0x1E, 0xA1, 0x41, 0x7D, 0x56, 0xF8, 0x71, 0x25, 0xBF, 0x3D, 0xCF,
+    0xF4, 0xC5, 0xB7, 0x75, 0x94, 0x3F, 0x0F, 0x8D, 0xF8, 0x45, 0x0E, 0x7C, 0x55, 0xEC, 0x16, 0xC7,
+    0xE1, 0xEC, 0x03, 0xCD, 0x8C, 0x4D, 0x3D, 0x93, 0x45, 0x8A, 0x1D, 0x3B, 0x6E, 0xDB, 0x05, 0xEA,
+    0x8B, 0x8D, 0xC7, 0x2C, 0x62, 0x90, 0x2E, 0x1E, 0x0A, 0xB5, 0xCD, 0xF0, 0xA7, 0x39, 0x76, 0x8C,
+    0xC2, 0x92, 0xE0, 0x63, 0x94, 0xA5, 0x70, 0xB6, 0x9D, 0xD6, 0x2F, 0xE7, 0x63, 0x9A, 0x58, 0x96,
+    0x41, 0x77, 0x0C, 0xA4, 0x8A, 0xA3, 0x0F, 0x52, 0x77, 0xA4, 0xAA, 0x24, 0xA7, 0xA8, 0x0F, 0x37,
+    0x1B, 0x7E, 0xDA, 0x32, 0xFE, 0x55, 0x83, 0x05, 0xA3, 0xB4, 0xEE, 0x15, 0x83, 0x57, 0x63, 0x91,
+    0x2F, 0xDA, 0x98, 0x11, 0x75, 0x62, 0xA1, 0xAF, 0x2F, 0xA5, 0xE4, 0x41, 0x88, 0x0F, 0x6F, 0x6F,
+    0x96, 0xE2, 0x44, 0xB7, 0xA5, 0x81, 0x88, 0xAD, 0x9B, 0x51, 0x9E, 0xF8, 0x32, 0xE2, 0x94, 0xED,
+    0x11, 0x40, 0x7D, 0xBC, 0xEE, 0xA9, 0x95, 0x8A, 0x47, 0xFF, 0x44, 0x05, 0x5E, 0xB8, 0x67, 0x31,
+    0x7E, 0x22, 0x74, 0x87, 0xC6, 0x40, 0x51, 0xAF, 0xEA, 0x90, 0x07, 0x5A, 0xB6, 0x80, 0xA1, 0x11,
+    0x45, 0x68, 0xD7, 0x01, 0x27, 0x4A, 0x61, 0x10, 0xF8, 0xB0, 0x12, 0xC2, 0x23, 0x60, 0x10, 0xCD,
+    0xC8, 0xD6, 0xC6, 0x47, 0x03, 0x9C, 0x76, 0xE0, 0x1B, 0x06, 0x76, 0x95, 0x3B, 0xE8, 0x83, 0xB7,
+    0x44, 0xC1, 0x53, 0xB3, 0x0A, 0x3E, 0x3F, 0x9E, 0x65, 0x1D, 0x5F, 0xB4, 0x3B, 0xC2, 0xE7, 0x07,
+    0xCB, 0x99, 0xB2, 0x64, 0x95, 0x52, 0xEA, 0xDE, 0xFB, 0xB9, 0x99, 0xD1, 0x47, 0x76, 0xEC, 0x5B,
+    0xFB, 0x5D, 0x34, 0x8B, 0xA1, 0x29, 0xEB, 0xBA, 0x83, 0xB6, 0x54, 0xD2, 0xEF, 0x1D, 0x79, 0x76,
+    0x02, 0x4F, 0x98, 0x52, 0x1E, 0x05, 0x4A, 0x05, 0x6F, 0x25, 0xEA, 0x69, 0x3D, 0xF9, 0x15, 0x2F,
+    0xE7, 0xCF, 0x3F, 0x0A, 0xBE, 0xE4, 0xBC, 0xF1, 0x21, 0xB3, 0xB0, 0x23, 0x94, 0x02, 0xCE, 0xF3,
+    0xB0, 0x50, 0xDC, 0x9B, 0x1E, 0x72, 0x52, 0x86, 0x13, 0x93, 0xA1, 0xDC, 0xDE, 0xD3, 0xE6, 0x3D,
+    0x97, 0x41, 0x21, 0xF1, 0x00, 0xF5, 0x2C, 0x0C, 0x0B, 0x6F, 0x14, 0x2F, 0xBA, 0x9A, 0x86, 0xFF,
+    0x3C, 0x06, 0x73, 0x71, 0x71, 0x41, 0x26, 0x7D, 0x4B, 0x5D, 0x68, 0xE5, 0xB1, 0x0B, 0x6B, 0x18,
+    0xCE, 0xE0, 0x99, 0x65, 0xFE, 0xA5, 0x8B, 0xF6, 0xC0, 0xC8, 0xB7, 0x64, 0x63, 0x4D, 0x97, 0xC0,
+    0x43, 0xE1, 0x69, 0x6C, 0xE4, 0xDD, 0xC1, 0x28, 0x32, 0x64, 0x81, 0x23, 0xB4, 0xED, 0x01, 0xFA,
+    0x83, 0xDC, 0x7D, 0xED, 0x40, 0x01, 0xFD, 0xC2, 0x75, 0x1E, 0x61, 0x14, 0x04, 0xCB, 0x47, 0x00,
+    0x97, 0x53, 0xDD, 0x84, 0x3C, 0x76, 0xEF, 0xE9, 0x96, 0x0C, 0xBD, 0x19, 0x56, 0x0A, 0x5B, 0xBC,
+    0xDF, 0x6A, 0xB5, 0x75, 0x43, 0xDB, 0x78, 0xA3, 0x10, 0x5A, 0x72, 0x72, 0x88, 0x05, 0x35, 0x2D,
+    0x3B, 0xD4, 0x01, 0x19, 0x2F, 0xFF, 0x52, 0x49, 0xF7, 0x3D, 0x87, 0x2E, 0x7C, 0x38, 0x80, 0xDF,
+    0x41, 0xC4, 0x3F, 0x4E, 0x78, 0xC9, 0xC8, 0xF3, 0x2D, 0xE3, 0xDB, 0x99, 0x4D, 0x33, 0x4F, 0x58,
+    0x69, 0xDF, 0x1D, 0xEC, 0x68, 0x2F, 0x60, 0xF0, 0x8C, 0x62, 0xD8, 0xAE, 0x7C, 0x8D, 0xC9, 0x87,
+    0x3D, 0x27, 0x2C, 0x2D, 0x46, 0x23, 0x8D, 0x2E, 0x1C, 0xA9, 0x21, 0x86, 0x24, 0xCF, 0xDB, 0x36,
+    0x8C, 0xF1, 0x8C, 0x23, 0x87, 0x83, 0x60, 0xAE, 0x3F, 0x6D, 0x42, 0xC8, 0x25, 0x67, 0xE5, 0x7B,
+    0x98, 0xD3, 0xA0, 0x28, 0x03, 0x08, 0x39, 0xF5, 0xE1, 0x1D, 0x5F, 0x1A, 0x55, 0x97, 0x70, 0x22,
+    0x46, 0x90, 0xBB, 0x4A, 0x1F, 0x39, 0x72, 0x77, 0xAB, 0xD0, 0xE7, 0x8F, 0xB4, 0x65, 0xD7, 0x26,
+    0x4E, 0x0D, 0xD3, 0xBD, 0xFF, 0x58, 0x14, 0x0C, 0x32, 0x35, 0x42, 0x19, 0x99, 0x8D, 0xFC, 0x1A,
+    0x6B, 0x40, 0x2E, 0x4E, 0xB6, 0x55, 0x86, 0x5F, 0x24, 0x84, 0x81, 0xFA, 0xE0, 0x6E, 0xF4, 0x9A,
+    0x8C, 0x1E, 0x14, 0xCC, 0x79, 0xBA, 0x3A, 0x5C, 0x7C, 0x6C, 0x0D, 0x2F, 0x1E, 0xF9, 0xBD, 0xBF,
+    0x03, 0x8E, 0x7D, 0x81, 0xC9, 0x9E, 0x62, 0xA3, 0xAF, 0x04, 0x5A, 0xE5, 0xCF, 0xA8, 0xE8, 0x89,
+    0xB5, 0x54, 0xC2, 0x99, 0xA7, 0x96, 0x9A, 0xF3, 0xDF, 0xBE, 0x94, 0xE7, 0x86, 0x65, 0x4A, 0x56,
+    0x4C, 0x08, 0x50, 0x99, 0xC5, 0xA1, 0x9E, 0xA0, 0x06, 0x52, 0x52, 0x0E, 0x1E, 0x81, 0xAF, 0x4B,
+    0x61, 0xFF, 0x53, 0xCC, 0xB2, 0x1A, 0xF6, 0x00, 0x2D, 0xAF, 0x43, 0xB2, 0xE7, 0x9F, 0x89, 0xCA,
+    0xB5, 0x41, 0x69, 0xB2, 0x0E, 0xAB, 0xA9, 0xDA, 0x95, 0xEF, 0xDE, 0x19, 0xDC, 0xA8, 0x9E, 0xDC,
+    0x59, 0x75, 0x50, 0x71, 0xB7, 0x38, 0xE8, 0xD9, 0xEE, 0x44, 0x16, 0xE7, 0xCD, 0xB9, 0xB8, 0xA8,
+    0xE4, 0xD1, 0x98, 0x48, 0xFC, 0xD2, 0xC3, 0xF8, 0x80, 0xE6, 0x05, 0x8E, 0x8F, 0x13, 0x59, 0xDC,
+    0x9D, 0x0D, 0x54, 0xF9, 0xC8, 0xA6, 0xD8, 0xF6, 0x5E, 0x07, 0xA0, 0xBE, 0x33, 0x0B, 0x67, 0x22,
+    0xB1, 0x51, 0xC6, 0x3E, 0xD8, 0xEC, 0x02, 0xC3, 0x9A, 0xC1, 0x63, 0xD7, 0x2E, 0xFA, 0xDC, 0x8E,
+    0x5E, 0x26, 0x12, 0x38, 0xE8, 0xDB, 0x08, 0xF3, 0x6D, 0x06, 0x06, 0x54, 0x8E, 0x2E, 0x7A, 0x0E,
+    0x28, 0x62, 0xED, 0xDB, 0xE3, 0x95, 0x50, 0x2B, 0x6E, 0x90, 0x28, 0x43, 0x25, 0xD9, 0x75, 0xDA,
+    0x04, 0x20, 0x4E, 0x65, 0x12, 0x17, 0x8B, 0x92, 0xBE, 0xD3, 0x02, 0xAC, 0xC2, 0x01, 0x29, 0xE5,
+    0x8A, 0xA6, 0x1C, 0xC9, 0x4F, 0x2C, 0x69, 0x43, 0x3A, 0xE8, 0x18, 0x06, 0x55, 0x71, 0xC6, 0x4C,
+    0x28, 0x5F, 0xE0, 0x1E, 0x33, 0x5A, 0x47, 0xB8, 0xA9, 0x84, 0xE3, 0xA9, 0x2A, 0xA7, 0x01, 0xC5,
+    0x4D, 0xC4, 0x75, 0x15, 0x44, 0xD5, 0xDF, 0x40, 0xEF, 0xE1, 0x8A, 0x38, 0x11, 0xC6, 0xC5, 0x13,
+    0x9A, 0x4E, 0xB6, 0x63, 0x2B, 0x6B, 0xF8, 0x6C, 0x39, 0xB3, 0x8A, 0x15, 0x94, 0x85, 0xE2, 0x71,
+    0x17, 0x68, 0x30, 0x33, 0xDD, 0x77, 0x18, 0x7F, 0x32, 0x15, 0x6A, 0xD2, 0x21, 0x21, 0xBD, 0x04,
+    0x5D, 0x5A, 0xD1, 0x97, 0xCF, 0xD2, 0x2E, 0xDD, 0x2F, 0x7D, 0x6C, 0x9E, 0x41, 0x47, 0x00, 0x4C,
+    0xC7, 0x3F, 0x98, 0xF7, 0x28, 0xBE, 0x4C, 0x7D, 0x5E, 0xA7, 0x39, 0xB5, 0xC2, 0x0E, 0x4C, 0x95,
+    0xA6, 0xF2, 0x45, 0x83, 0xEA, 0xDC, 0x4C, 0x59, 0xFC, 0x86, 0x94, 0xD4, 0xE9, 0xDB, 0xE5, 0x62,
+    0x6C, 0xFD, 0x0B, 0x9E, 0x29, 0x19, 0x89, 0xDD, 0x80, 0x38, 0x0A, 0xA4, 0xA4, 0x5D, 0x65, 0xE3,
+    0xE0, 0x8C, 0x3B, 0x53, 0x37, 0x9C, 0x89, 0x57, 0xCA, 0xF3, 0xA0, 0x2D, 0xB6, 0x74, 0x6B, 0x60,
+    0x4C, 0x59, 0xFB, 0xC4, 0xD6, 0xBB, 0xB0, 0x67, 0x58, 0xF2, 0x85, 0x45, 0xEA, 0x24, 0x4D, 0xAD,
+    0xAB, 0xA1, 0xF0, 0x97, 0x68, 0xE7, 0xF0, 0x6F, 0x73, 0x6D, 0xC1, 0x02, 0x44, 0x87, 0xC4, 0x98,
+    0xDE, 0x0E, 0xF1, 0x6C, 0x1C, 0x9D, 0x78, 0x04, 0x5E, 0x81, 0xE6, 0x25, 0x2E, 0xBA, 0x9F, 0x57,
+    0xD8, 0xAE, 0xB5, 0x44, 0x22, 0x56, 0x62, 0x5D, 0x88, 0x23, 0xBD, 0x90, 0xA7, 0xCF, 0x74, 0xFF,
+    0xCF, 0xDB, 0x87, 0xFC, 0xD9, 0x79, 0x68, 0xC4, 0xBB, 0x13, 0xFC, 0xB2, 0x7A, 0xBD, 0x4B, 0xE9,
+    0x6E, 0x33, 0xEF, 0xB2, 0xFE, 0x47, 0x90, 0x03, 0x4C, 0xC7, 0xEF, 0xF9, 0x65, 0x4C, 0x53, 0x2F,
+    0x00, 0x83, 0x6B, 0x3E, 0xE0, 0xCF, 0xDF, 0xD9, 0x4C, 0x5E, 0x2C, 0x40, 0x4F, 0x0B, 0x91, 0x0F,
+    0xA7, 0xB7, 0x16, 0x9D, 0x8A, 0xDC, 0x03, 0x65, 0xB6, 0x8F, 0x43, 0x41, 0x77, 0x3E, 0x8D, 0x66,
+    0x86, 0xCD, 0x60, 0x61, 0xF8, 0xE3, 0x0D, 0x9A, 0xA2, 0x99, 0x6D, 0x05, 0xA2, 0xCB, 0x08, 0x18,
+    0xF3, 0xC2, 0xB8, 0x25, 0x46, 0xF9, 0x16, 0xAB, 0x72, 0x34, 0x3C, 0x52, 0x4C, 0x2E, 0xA4, 0x85,
+    0xA9, 0x83, 0x3E, 0xF7, 0xDD, 0xBC, 0xF8, 0x81, 0x04, 0x80, 0x4B, 0x1D, 0xD9, 0x65, 0x9A, 0xF8,
+    0xF4, 0xE0, 0x75, 0xCE, 0xA7, 0x48, 0xF9, 0x23, 0xE0, 0xF5, 0xEF, 0xFA, 0xC5, 0xE4, 0x6A, 0x13,
+    0xE6, 0x76, 0xEF, 0xF6, 0x3D, 0x25, 0x7A, 0x2E, 0x6B, 0x53, 0xE6, 0x88, 0xD2, 0x84, 0x87, 0x46,
+    0x81, 0xA3, 0x02, 0x81, 0x16, 0x37, 0xAD, 0x3E, 0x13, 0x94, 0x07, 0xE8, 0x3A, 0x70, 0x09, 0x39,
+    0xED, 0x77, 0x72, 0xB8, 0xBB, 0xAE, 0x40, 0x63, 0x84, 0xD7, 0xF2, 0x28, 0xDD, 0x19, 0x60, 0x3F,
+    0xA3, 0xA0, 0x28, 0x8A, 0xF1, 0xF7, 0x0C, 0x8F, 0xD4, 0x56, 0xC1, 0xB3, 0x73, 0x24, 0xFD, 0xC4,
+    0xA2, 0x5D, 0xDA, 0xFC, 0xEF, 0xAA, 0xCB, 0x06, 0xB2, 0x52, 0xB7, 0xC5, 0xBD, 0x5A, 0x0B, 0xC2,
+    0x9B, 0x6F, 0xC3, 0x99, 0x89, 0x7D, 0xC0, 0xCF, 0x9C, 0x06, 0xEF, 0xD4, 0xB0, 0x97, 0x16, 0x28,
+    0x20, 0x05, 0x4E, 0xE3, 0x64, 0x32, 0x70, 0x23, 0x09, 0x92, 0x10, 0x09, 0xAA, 0xBE, 0xC3, 0x52,
+    0xD9, 0xAE, 0xC6, 0xC2, 0x23, 0x84, 0x49, 0xDD, 0x9D, 0xF1, 0xF7, 0xA8, 0xA2, 0xA3, 0x79, 0x78,
+    0xB1, 0x4C, 0x08, 0xF4, 0x99, 0x1F, 0x59, 0xE9, 0x54, 0xE5, 0x6D, 0x85, 0x52, 0x01, 0x17, 0x18,
+    0x06, 0xFE, 0x33, 0x7E, 0xF7, 0x88, 0xFA, 0xB7, 0xB9, 0xEA, 0xD3, 0x71, 0x6F, 0x67, 0x9E, 0x6F,
+    0xD7, 0x16, 0x58, 0x1A, 0x00, 0x12, 0x83, 0xA9, 0x10, 0x21, 0xD3, 0xAB, 0xD3, 0x25, 0xE8, 0xE0,
+    0xFA, 0x04, 0x26, 0xA8, 0x34, 0xCC, 0xFA, 0x83, 0x88, 0x46, 0x11, 0x52, 0xB1, 0x44, 0x51, 0x6A,
+    0x47, 0x49, 0x9F, 0xA1, 0x02, 0x6F, 0xC0, 0xDB, 0x6E, 0x9D, 0xD9, 0xD2, 0xC2, 0x6E, 0x6D, 0x18,
+    0xC6, 0x68, 0xC9, 0x82, 0xF9, 0x53, 0x44, 0x89, 0x57, 0xE0, 0xD2, 0x56, 0x77, 0xE2, 0xB3, 0x6D,
+    0xE8, 0xD1, 0x56, 0x3E, 0xF8, 0x5C, 0xB4, 0x19, 0x54, 0x34, 0xAB, 0x37, 0x27, 0x64, 0x30, 0xD5,
+    0xAC, 0xD7, 0x5E, 0xAE, 0x5D, 0xE9, 0xA9, 0x37, 0x24, 0x15, 0xCC, 0x6A, 0x42, 0x2B, 0x37, 0x1B,
+    0xD7, 0x9D, 0x07, 0x05, 0x33, 0xC6, 0xDA, 0x23, 0x5D, 0x48, 0x08, 0xF7, 0x7E, 0xD3, 0x10, 0xCF,
+    0x22, 0x03, 0x3A, 0x37, 0x68, 0x1A, 0xCB, 0x1D, 0xA2, 0xC8, 0x4A, 0x60, 0x09, 0x4D, 0xA7, 0xBE,
+    0x66, 0x9E, 0x50, 0x71, 0xF5, 0x5B, 0x7E, 0xDA, 0xD4, 0xBB, 0x45, 0x16, 0xB7, 0xCD, 0x3F, 0x60,
+    0xD3, 0xA1, 0xC5, 0x86, 0x17, 0x38, 0x21, 0xEF, 0x3B, 0x5E, 0x28, 0xEB, 0x35, 0xBB, 0x1F, 0x46,
+    0x19, 0xCE, 0xE5, 0x60, 0x76, 0x8F, 0xC2, 0x46, 0xBC, 0xF6, 0x4A, 0x7C, 0x37, 0xA5, 0x43, 0x8A,
+    0x9D, 0x69, 0x7E, 0x6E, 0x5C, 0x59, 0xF8, 0x87, 0x08, 0xC1, 0xDA, 0xA6, 0xA6, 0x2C, 0x0E, 0x42,
+    0xA7, 0x26, 0x8D, 0x15, 0xE0, 0x9A, 0x9B, 0x91, 0xCB, 0xE5, 0x92, 0xF4, 0xD5, 0xF5, 0xF7, 0xEF,
+    0x93, 0x18, 0xF3, 0x21, 0x1B, 0x55, 0x6E, 0xE1, 0xDB, 0x5F, 0x64, 0x0E, 0xAC, 0x99, 0x3A, 0xEA,
+    0xFF, 0xA5, 0x22, 0x35, 0x52, 0x77, 0xD3, 0x0A, 0x6A, 0xF7, 0x2C, 0x2C, 0xDD, 0x98, 0x89, 0xD7,
+    0xFC, 0x70, 0xCB, 0x39, 0x2D, 0xCA, 0x77, 0x1E, 0x36, 0x2B, 0x61, 0x83, 0x0F, 0x42, 0xBB, 0x16,
+    0x3F, 0x4F, 0x93, 0xCC, 0xE1, 0xE5, 0x06, 0x23, 0xB7, 0x22, 0xBE, 0xB8, 0x10, 0xAF, 0x7B, 0x2E,
+    0x52, 0x35, 0xBE, 0xB1, 0x61, 0x1A, 0xDA, 0x82, 0x51, 0x9A, 0xFE, 0x4C, 0x07, 0xA8, 0xFC, 0x44,
+    0xBE, 0x2A, 0xE3, 0x46, 0x92, 0x69, 0xA7, 0x74, 0x83, 0xDC, 0x7F, 0x10, 0xA2, 0x9E, 0xA4, 0x84,
+    0x44, 0x31, 0x98, 0xEA, 0x76, 0x6B, 0x32, 0x77, 0x17, 0xA6, 0xFD, 0x91, 0x44, 0x93, 0xBF, 0x96,
+    0x04, 0x41, 0x28, 0x77, 0x60, 0x49, 0xFA, 0xB8, 0x53, 0x22, 0x3A, 0x8E, 0x3A, 0x0E, 0x2E, 0x0D,
+    0xB4, 0x30, 0x3C, 0xA8, 0x22, 0xA5, 0xEF, 0x88, 0x29, 0xCE, 0xB4, 0x60, 0xE7, 0x0C, 0x18, 0xD3,
+    0xCC, 0xA3, 0x8F, 0x94, 0x3C, 0x90, 0x1A, 0xCC, 0x63, 0x73, 0x50, 0x72, 0xF5, 0xEC, 0x98, 0x9F,
+    0xB9, 0x03, 0x9D, 0x14, 0x0E, 0x76, 0x54, 0x68, 0xD9, 0x12, 0x0D, 0xAA, 0x86, 0x63, 0x6E, 0x62,
+    0x09, 0x64, 0x54, 0x38, 0x08, 0x10, 0xF2, 0xB3, 0x9E, 0xD4, 0xB4, 0xDE, 0x63, 0x68, 0xB3, 0xB8,
+    0x9E, 0x7F, 0xC4, 0xB8, 0xD7, 0x50, 0x78, 0xE9, 0x30, 0xFA, 0x86, 0x44, 0x2C, 0x29, 0x80, 0x53,
+    0xDE, 0x9A, 0xCD, 0x61, 0x9B, 0x5A, 0x46, 0x95, 0xA7, 0xCD, 0xEF, 0xDB, 0x88, 0xF6, 0xA6, 0x76,
+    0xE1, 0x7E, 0xCF, 0x86, 0x0F, 0x68, 0x48, 0x06, 0xE9, 0x8F, 0x31, 0xE7, 0x56, 0x35, 0x5B, 0x57,
+    0xA4, 0x61, 0x5E, 0x71, 0xC0, 0xC6, 0xA9, 0xBC, 0xD4, 0x69, 0x1B, 0x54, 0xDC, 0x4F, 0xEB, 0x9D,
+    0x35, 0xDB, 0xEE, 0xD2, 0x39, 0xB8, 0x81, 0xDB, 0x73, 0x5E, 0xB3, 0x30, 0xF7, 0xA2, 0x66, 0xC4,
+    0xE7, 0xD6, 0x83, 0x2F, 0x36, 0x70, 0x85, 0x97, 0x2D, 0x39, 0xE9, 0x17, 0x4C, 0x6F, 0x52, 0x95,
+    0x80, 0x79, 0x65, 0x54, 0xD0, 0xFD, 0xB8, 0xA8, 0xF2, 0x7B, 0x46, 0xA1, 0x75, 0xCD, 0x59, 0x93,
+    0x69, 0x1D, 0xCC, 0xC4, 0xB1, 0x38, 0x18, 0xB8, 0x70, 0x50, 0x9D, 0xD7, 0x37, 0x97, 0xFB, 0x6A,
+    0xDD, 0x3C, 0x8F, 0x28, 0x41, 0xB8, 0x53, 0xD3, 0x3E, 0x7B, 0xB9, 0x9F, 0xAA, 0x5B, 0x3F, 0x62,
+    0x1D, 0x5E, 0xDA, 0xBF, 0xD7, 0xC0, 0x73, 0xD6, 0x10, 0x47, 0x0F, 0x2D, 0x71, 0x4B, 0x5E, 0xCC,
+    0x9B, 0x0D, 0xD7, 0xCE, 0xEC, 0x2F, 0x8F, 0xE3, 0xE3, 0x77, 0x6C, 0x73, 0xE4, 0x30, 0x79, 0x73,
+    0x2D, 0xC3, 0x64, 0x12, 0x44, 0x71, 0x7C, 0xCC, 0x31, 0x39, 0xA9, 0x93, 0x42, 0x54, 0x45, 0x0E,
+    0x3C, 0xD9, 0xBF, 0x2C, 0x26, 0x6C, 0x7B, 0x86, 0x1F, 0x0F, 0x57, 0x4B, 0xE4, 0x78, 0xBC, 0xAB,
+    0xF5, 0x7A, 0x36, 0x14, 0x87, 0x75, 0xEB, 0x99, 0xAE, 0xC6, 0x6E, 0x68, 0x68, 0xBE, 0xCD, 0x27,
+    0x77, 0x91, 0xDB, 0x88, 0x3A, 0x3B, 0xF9, 0x8D, 0xE7, 0x64, 0x02, 0x37, 0xE2, 0xA0, 0x0F, 0x95,
+    0x05, 0xB8, 0x2F, 0x7D, 0x22, 0xBC, 0x4E, 0x5F, 0x13, 0x13, 0xEF, 0xF1, 0x10, 0xDA, 0x6B, 0xB4,
+    0x36, 0x2A, 0xD4, 0x8F, 0x63, 0x2F, 0xC1, 0xEC, 0xE3, 0x1B, 0x8B, 0x2D, 0x87, 0x5C, 0xD0, 0x5E,
+    0x22, 0xB3, 0x40, 0x6E, 0x8D, 0xFA, 0x06, 0x65, 0xA3, 0xC7, 0x55, 0x53, 0xE1, 0x3C, 0xE3, 0xF5,
+    0x98, 0x9D, 0x67, 0x52, 0xD3, 0x9D, 0x5F, 0xBC, 0x6C, 0x5F, 0xA5, 0x06, 0xF1, 0xA2, 0xAE, 0xD7,
+    0x46, 0xA5, 0x71, 0x68, 0x33, 0xA6, 0x49, 0x16, 0x53, 0x0F, 0x5C, 0x9A, 0xF2, 0xBC, 0x50, 0xCD,
+    0xEF, 0xE7, 0x65, 0x44, 0xAF, 0x9E, 0x32, 0x39, 0x94, 0xDE, 0x95, 0x7F, 0xB3, 0xAC, 0xAE, 0x77,
+    0x9B, 0xCF, 0xDD, 0x50, 0x74, 0xFC, 0x21, 0x00, 0xCC, 0x9A, 0x53, 0xB5, 0xCE, 0x78, 0x21, 0xC3,
+    0xC3, 0x09, 0xB3, 0x3E, 0x10, 0x10, 0x6D, 0xC5, 0x1F, 0xCA, 0x35, 0x3A, 0xD2, 0xF9, 0x28, 0x57,
+    0x83, 0x72, 0xB3, 0x73, 0xA1, 0xFA, 0x69, 0xD7, 0x6D, 0x9C, 0x1F, 0x78, 0x77, 0xCF, 0x17, 0x04,
+    0xCD, 0x08, 0x49, 0x7C, 0x04, 0x94, 0x17, 0xE4, 0x82, 0xD8, 0xF3, 0xBB, 0xC9, 0x4B, 0xC9, 0x35,
+    0x93, 0xD7, 0x35, 0x7C, 0x03, 0x65, 0xD4, 0x70, 0x46, 0xCD, 0x38, 0x9A, 0x5F, 0x66, 0x4A, 0x5F,
+    0xFD, 0xEC, 0x35, 0x9E, 0x8A, 0x90, 0x0D, 0x3F, 0xEB, 0x43, 0xD0, 0x6C, 0x86, 0xA9, 0x90, 0x72,
+    0x93, 0x47, 0xBA, 0x80, 0xD4, 0xC4, 0xE8, 0xC7, 0x1E, 0x6A, 0xA8, 0xB6, 0x70, 0x25, 0x23, 0x48,
+    0x74, 0xC4, 0x97, 0xA8, 0x99, 0x2D, 0xFC, 0xA2, 0x38, 0xC9, 0x64, 0x9A, 0x6A, 0x5D, 0xD3, 0x15,
+    0x80, 0x11, 0xAE, 0xF4, 0x43, 0x63, 0xFD, 0xFA, 0x6D, 0x31, 0x11, 0x4B, 0x05, 0x39, 0x62, 0xD7,
+    0x8A, 0x9F, 0xA4, 0x04, 0x1A, 0x59, 0x69, 0xFE, 0xFE, 0xAB, 0xD6, 0x77, 0x4C, 0xF6, 0x39, 0xC7,
+    0x8A, 0x8A, 0x8F, 0xB1, 0x74, 0x50, 0x40, 0x4D, 0x63, 0x68, 0xA5, 0xBC, 0xEF, 0x13, 0x17, 0xC7,
+    0xCA, 0x92, 0xA5, 0x7C, 0xE9, 0xC5, 0xAC, 0x68, 0x83, 0xB0, 0xE6, 0x17, 0x76, 0x46, 0xBF, 0xD5,
+    0x16, 0x07, 0xEF, 0xF8, 0x7E, 0x60, 0xB4, 0x25, 0xDF, 0xD6, 0x2D, 0x52, 0x71, 0x67, 0xAA, 0x78,
+    0xF0, 0xB8, 0xF7, 0x41, 0xD8, 0xE6, 0xEE, 0x19, 0xC4, 0x22, 0xE5, 0x74, 0xA7, 0x63, 0xB6, 0x32,
+    0xBD, 0xE4, 0x76, 0x69, 0x6B, 0x2A, 0x2D, 0x0E, 0x78, 0xC6, 0x04, 0x36, 0x44, 0x2D, 0xD5, 0xEE,
+    0xF2, 0x2C, 0x0A, 0xE7, 0xAB, 0xF7, 0x30, 0x6D, 0x70, 0xCE, 0xB9, 0x6B, 0x0F, 0xA9, 0xC1, 0x73,
+    0x4A, 0x80, 0xDF, 0x0B, 0x3B, 0x09, 0x55, 0xB6, 0x78, 0x0A, 0x1B, 0x78, 0x94, 0xA2, 0xA6, 0xD1,
+    0xF4, 0x10, 0x64, 0x67, 0x1D, 0xF4, 0x45, 0xE6, 0xEA, 0x06, 0xDC, 0xBD, 0x56, 0xB5, 0xD8, 0xD2,
+    0xBF, 0x3D, 0xF8, 0x48, 0xE1, 0x1C, 0xA9, 0xEF, 0xDC, 0xF5, 0xF5, 0x0B, 0x00, 0x46, 0x80, 0x6B,
+    0x51, 0x88, 0x9D, 0x1D, 0xD9, 0x9F, 0xD5, 0x27, 0x4B, 0xA2, 0x5B, 0x10, 0x95, 0x6A, 0x4A, 0x2C,
+    0x51, 0x83, 0xA4, 0xEE, 0x44, 0x48, 0x7A, 0xB2, 0x55, 0x5F, 0xAA, 0xC7, 0x9D, 0xDD, 0x19, 0xAE,
+    0x99, 0xBE, 0x62, 0xC9, 0x82, 0x7E, 0x5A, 0xF9, 0x5E, 0xF9, 0xD9, 0xEC, 0x5A, 0xEF, 0xB4, 0x04,
+    0x67, 0xBC, 0xDB, 0x30, 0x40, 0x34, 0xEF, 0x18, 0x49, 0xA4, 0xE6, 0x67, 0xF4, 0x71, 0x7B, 0x2C,
+    0x8D, 0xDF, 0x74, 0x8E, 0xAB, 0xDA, 0x24, 0x4A, 0xA2, 0xEB, 0x8B, 0xBF, 0xA8, 0xAD, 0x0D, 0x7F,
+    0x9F, 0x58, 0xA6, 0xA4, 0xA2, 0x4B, 0xFF, 0x60, 0xD2, 0xA3, 0xE9, 0x89, 0xFF, 0x4E, 0x04, 0x1E,
+    0x24, 0x1B, 0xA8, 0xF7, 0xDF, 0xC0, 0x56, 0x2A, 0x4B, 0xD9, 0x3C, 0xDA, 0xF5, 0x54, 0x9C, 0x67,
+    0xC9, 0xCA, 0x25, 0x44, 0x2E, 0xBC, 0x79, 0xED, 0xBD, 0xC2, 0x87, 0xB2, 0x2F, 0x04, 0x68, 0x5F,
+    0x8A, 0xA8, 0xE7, 0xEE, 0x9A, 0x01, 0xE8, 0xCE, 0x41, 0xAA, 0x48, 0x72, 0x2A, 0xD6, 0xFE, 0x29,
+    0xE9, 0x88, 0x8B, 0x6E, 0x9C, 0x7C, 0xFE, 0x45, 0x8C, 0xE7, 0x26, 0x49, 0x69, 0x67, 0xAC, 0x6F,
+    0x1B, 0xBE, 0x2D, 0xC3, 0x4E, 0x35, 0xA5, 0x8D, 0x1F, 0xC7, 0xA2, 0xA1, 0xA7, 0x69, 0x23, 0xD7,
+    0x39, 0x0D, 0x1E, 0xE4, 0x97, 0x41, 0x03, 0x14, 0x75, 0x7F, 0xC4, 0x97, 0x04, 0x91, 0x2A, 0x71,
+    0x6C, 0x9A, 0x8C, 0x2C, 0x5F, 0xB3, 0x2D, 0xE6, 0x35, 0x1E, 0xD0, 0x64, 0x3C, 0x88, 0x4F, 0x27,
+    0x24, 0xD9, 0x39, 0xCD, 0xBD, 0x87, 0xD2, 0x27, 0x61, 0x7A, 0xF1, 0xCE, 0xCD, 0xDD, 0x92, 0x2E,
+    0x43, 0x7E, 0x28, 0x40, 0x27, 0x99, 0xF2, 0x79, 0x86, 0x22, 0xEB, 0x9C, 0x30, 0xF1, 0x1A, 0x73,
+    0x4C, 0x6E, 0x4B, 0xB3, 0xA2, 0x8E, 0x87, 0x72, 0xED, 0x4C, 0xCF, 0x00, 0x03, 0xEC, 0xE5, 0x10,
+    0x99, 0xAE, 0x39, 0x7C, 0xF2, 0xC9, 0x3C, 0x0B, 0xCA, 0xC8, 0xA2, 0x0D, 0x3D, 0xA7, 0x72, 0xB8,
+    0x85, 0x53, 0xD8, 0x84, 0xCC, 0x59, 0x15, 0x0C, 0x6C, 0xEC, 0x17, 0x24, 0x5B, 0xA2, 0x79, 0x29,
+    0x9F, 0x74, 0x0E, 0xBF, 0x02, 0xE8, 0x27, 0x83, 0x6D, 0x8A, 0x36, 0x62, 0x91, 0xF0, 0x96, 0x99,
+    0xD8, 0x14, 0x75, 0x93, 0xB7, 0xAF, 0x41, 0x2F, 0xE3, 0xD7, 0x14, 0x16, 0xFC, 0x29, 0xFA, 0x2A,
+    0xB6, 0x1B, 0x07, 0x4E, 0x8C, 0x61, 0xA1, 0xF0, 0x8E, 0x66, 0x7D, 0x28, 0xCF, 0x57, 0x1B, 0x5A,
+    0x81, 0x3F, 0xCE, 0x95, 0xD2, 0x1C, 0xA3, 0x39, 0x0B, 0x0D, 0xA7, 0x94, 0x84, 0xEC, 0x65, 0x6D,
+    0x75, 0xF7, 0x98, 0xD1, 0xB9, 0x5F, 0x6E, 0x81, 0x01, 0xDE, 0xE1, 0xD0, 0x0C, 0xAA, 0xE9, 0xE5,
+    0xF0, 0x68, 0xA1, 0xA4, 0x81, 0xF0, 0xA9, 0xAF, 0x52, 0x11, 0x44, 0x42, 0x01, 0x9C, 0x0D, 0xED,
+    0xA6, 0x5A, 0x4A, 0x55, 0xA7, 0xD5, 0x26, 0x8C, 0x4D, 0xF8, 0x60, 0xAE, 0xD3, 0xFC, 0x3C, 0xC9,
+    0xCB, 0x24, 0xC2, 0x40, 0x19, 0x3D, 0x95, 0x35, 0xDA, 0xEC, 0xF1, 0xA7, 0xF8, 0x2D, 0x98, 0x49,
+    0x49, 0x9E, 0xBB, 0x49, 0x65, 0x78, 0x34, 0x89, 0xAE, 0x3F, 0x8C, 0xFD, 0x21, 0xA3, 0xA5, 0x37,
+    0xEA, 0x10, 0x17, 0x4A, 0xE7, 0xDC, 0x7C, 0x98, 0x78, 0x2A, 0x4C, 0x2F, 0x62, 0xD8, 0x00, 0xC6,
+    0x8F, 0x21, 0x9B, 0x82, 0xF9, 0xC1, 0xD7, 0x15, 0x14, 0xBF, 0x89, 0xDA, 0x69, 0x39, 0x08, 0x04,
+    0x5A, 0xCA, 0x9B, 0x08, 0x28, 0x68, 0x48, 0xC5, 0xB6, 0xDA, 0x84, 0x28, 0xA9, 0x19, 0x94, 0x4A,
+    0xE0, 0x43, 0xAE, 0x38, 0x5D, 0xEF, 0x22, 0xF0, 0x21, 0x0C, 0x14, 0x45, 0x90, 0x9E, 0x9E, 0xAB,
+    0x5A, 0xF5, 0x5D, 0x25, 0x12, 0x40, 0xB5, 0xD0, 0xD2, 0x90, 0x5C, 0xC7, 0xAF, 0xB1, 0xF6, 0x63,
+    0xD6, 0x69, 0xCF, 0x08, 0x7E, 0x02, 0xFD, 0x00, 0x2F, 0x3A, 0x78, 0x24, 0xF2, 0xF3, 0xF2, 0x49,
+    0x61, 0x38, 0x7F, 0xAF, 0xC9, 0x87, 0x55, 0xEE, 0xBC, 0x65, 0x2D, 0x21, 0xC8, 0xA5, 0x1C, 0x3F,
+    0x3F, 0xFC, 0xE9, 0xF0, 0x3C, 0xC0, 0x23, 0x4C, 0x48, 0xE5, 0x98, 0x40, 0x5B, 0x9F, 0xE5, 0xA0,
+    0x16, 0x3E, 0x3A, 0x18, 0x6B, 0x26, 0x8D, 0x7C, 0x1B, 0xF5, 0xE2, 0x32, 0xBB, 0x3C, 0x52, 0xB2,
+    0x1F, 0x69, 0xFE, 0x57, 0x6C, 0xB2, 0x24, 0x03, 0x2C, 0x29, 0xEA, 0x46, 0x0E, 0x4C, 0xAC, 0x15,
+    0x56, 0xB6, 0xD6, 0x36, 0x05, 0xC8, 0x97, 0xF7, 0x49, 0x5E, 0xFB, 0xDA, 0xC4, 0x03, 0x34, 0x34,
+    0xAB, 0x21, 0x22, 0x03, 0xD9, 0x27, 0x63, 0x73, 0x4E, 0xA9, 0x78, 0xC8, 0xC2, 0xE9, 0xCF, 0xB3,
+    0x31, 0x54, 0xB4, 0x43, 0x9B, 0xDB, 0x80, 0x03, 0x52, 0x46, 0x8D, 0xDC, 0x94, 0xCB, 0xB6, 0xE0,
+    0x4E, 0x99, 0x7E, 0x22, 0x3E, 0x2F, 0x16, 0x13, 0xD6, 0x8B, 0xDF, 0x3B, 0x9F, 0xA9, 0x2A, 0x26,
+    0xED, 0xCD, 0x46, 0xDF, 0x23, 0x95, 0x29, 0x65, 0xF9, 0xD5, 0x3E, 0xDE, 0x4F, 0xA8, 0x1E, 0x77,
+    0xA8, 0x4A, 0x50, 0x42, 0x4D, 0x9F, 0x4B, 0x7B, 0xA1, 0x79, 0x52, 0xF8, 0x46, 0x01, 0xED, 0xC1,
+    0x01, 0xDD, 0x12, 0x0A, 0x8A, 0xEB, 0x48, 0x0A, 0xB2, 0xB6, 0x4B, 0x6D, 0x8D, 0xF2, 0x06, 0x5C,
+    0x8A, 0xB1, 0xE7, 0x5A, 0xAB, 0x12, 0xDF, 0x68, 0x3C, 0xA1, 0x95, 0x3C, 0xC6, 0xAD, 0x9D, 0x7A,
+    0x1A, 0x43, 0xB5, 0x2C, 0xAE, 0x9A, 0x67, 0xFF, 0xA8, 0x19, 0x84, 0xF7, 0x5A, 0x47, 0x5C, 0x97,
+    0xFB, 0x4F, 0xA8, 0xC0, 0xF1, 0xE5, 0x86, 0xBA, 0xEC, 0xB4, 0x04, 0x29, 0xA7, 0xAA, 0x12, 0xEA,
+    0x1A, 0xC2, 0xDC, 0x0D, 0x62, 0x21, 0xE1, 0x77, 0xB9, 0xB2, 0x4C, 0xCF, 0x35, 0x86, 0x63, 0xD3,
+    0x36, 0xA9, 0x0E, 0x31, 0xAC, 0x39, 0xC8, 0x76, 0xA9, 0xE9, 0x8C, 0xC3, 0xE3, 0x3C, 0x79, 0x4E,
+    0x14, 0x21, 0x4B, 0xDE, 0x6D, 0xC4, 0xE9, 0xC9, 0x75, 0xBA, 0x9C, 0x2D, 0x14, 0xD4, 0xB2, 0xFF
+};
+
+const uint32_t data_crc = 0x7322E75E;
+
+#endif

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

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

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

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

+ 2 - 0
test/unittest/test_main.cpp

@@ -5,3 +5,5 @@
 
 
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 
 
+//todo this should not be necessary - disable ymodem for the unittest or implement a unittest module
+#include "src/shellmatta_ymodem.c"