Commit 2edc30cf authored by jan.koester's avatar jan.koester
Browse files

fixed

parent 8836c115
Loading
Loading
Loading
Loading
+226 −66
Original line number Diff line number Diff line
@@ -39,8 +39,154 @@
#include <winreg.h>


/*******************************************************************************
 * Copyright (c) 2025, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 * names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include "regedit.h"
#include "exception.h" // Angenommen: Hier ist ConfException definiert

#include <iostream>
#include <string>
#include <sstream>
#include <stdexcept>
#include <algorithm>

// Spezifische Windows API Includes
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winreg.h>


namespace confplus {   
    // --- Implementierung der Registry-Klasse ---
    /**
     * @brief Rekursive Funktion zum Laden von Werten und Subkeys aus der Registry.
     * @param conf Das Config-Objekt, das gefüllt wird.
     * @param hKey Das Handle des aktuell geöffneten Registry-Schlüssels.
     * @param currentConfigPath Der aktuelle Pfad im Config-Objekt (z.B. "/HTTP").
     */
    void loadKeyRecursiveA(Config* conf, HKEY hKey, const std::string& currentConfigPath) {
        
        DWORD dwValues, dwSubKeys, dwMaxValueNameLen, dwMaxValueLen, dwMaxSubKeyLen;
        
        // 1. Hole Infos über den aktuellen Schlüssel
        LONG lRes = RegQueryInfoKeyA(
            hKey, NULL, NULL, NULL, 
            &dwSubKeys, &dwMaxSubKeyLen, NULL, 
            &dwValues, &dwMaxValueNameLen, &dwMaxValueLen, 
            NULL, NULL
        );

        if (lRes != ERROR_SUCCESS) {
            // Fehler beim Abfragen ist nicht kritisch, einfach zurückkehren
            return; 
        }
        
        // --- 2. Werte (Values) laden (z.B. PORT unter /HTTP) ---
        if (dwValues > 0) {
            // Puffer allozieren (+1 für Nullterminierung)
            char* valueNameBuffer = new char[dwMaxValueNameLen + 1];
            char* dataBuffer = new char[dwMaxValueLen + 1]; 
            DWORD dwSizeValueName, dwSizeData, dwType;

            for (DWORD i = 0; i < dwValues; ++i) {
                dwType = 0;
                dwSizeValueName = dwMaxValueNameLen + 1; 
                dwSizeData = dwMaxValueLen + 1;

                lRes = RegEnumValueA(
                    hKey, i, 
                    valueNameBuffer, &dwSizeValueName, 
                    NULL, &dwType, 
                    (LPBYTE)dataBuffer, &dwSizeData
                );

                if (lRes == ERROR_SUCCESS) {
                    // Konstruiere den vollen Konfigurationspfad: /AktuellerPfad/ValueName
                    std::string cname = currentConfigPath + "/" + std::string(valueNameBuffer);
                    std::string cvalue;

                    if (dwType == REG_SZ) {
                        cvalue = std::string(dataBuffer);
                    } else if (dwType == REG_DWORD) {
                        std::stringstream ss;
                        // Daten sind little-endian in dataBuffer
                        ss << *((DWORD*)dataBuffer);
                        cvalue = ss.str();
                    } else {
                        continue; // Andere Typen ignorieren
                    }
                    
                    // Füge den Wert in das Config-Objekt ein
                    Config::ConfigData* ckey = conf->setKey(cname);
                    conf->setValue(ckey, 0, cvalue);
                }
            }
            delete[] valueNameBuffer;
            delete[] dataBuffer;
        }

        // --- 3. Subkeys (Unterschlüssel) rekursiv laden (z.B. HTTP unter /BLOGI) ---
        if (dwSubKeys > 0) {
            char* subKeyNameBuffer = new char[dwMaxSubKeyLen + 1];
            HKEY hSubKey;

            for (DWORD i = 0; i < dwSubKeys; ++i) {
                DWORD dwSizeSubKeyName = dwMaxSubKeyLen + 1;
                
                lRes = RegEnumKeyExA(
                    hKey, i, 
                    subKeyNameBuffer, &dwSizeSubKeyName, 
                    NULL, NULL, NULL, NULL
                );

                if (lRes == ERROR_SUCCESS) {
                    // Öffne den Subkey
                    lRes = RegOpenKeyExA(
                        hKey, subKeyNameBuffer, 0, KEY_READ, &hSubKey
                    );

                    if (lRes == ERROR_SUCCESS) {
                        // Erzeuge den neuen Konfigurationspfad
                        std::string nextConfigPath = currentConfigPath + "/" + std::string(subKeyNameBuffer);
                        
                        // Rekursiver Aufruf
                        loadKeyRecursiveA(conf, hSubKey, nextConfigPath);
                        
                        RegCloseKey(hSubKey);
                    }
                }
            }
            delete[] subKeyNameBuffer;
        }
    }

    // -------------------------------------------------------------------------
    // --- Implementierung der Registry-Klasse ---------------------------------
    // -------------------------------------------------------------------------
    
    Registry::Registry(){}

@@ -58,30 +204,96 @@ namespace confplus {
        return "Jan Koester";
    }

    // Hilfsfunktion zum Aufteilen des Pfades in Schlüssel und Wertnamen
    /**
     * @brief Splittet den Config-Pfad (/Abschnitt/Key) in den Registry Subkey-Pfad (Abschnitt\Key)
     * und den letzten Teil als Value-Namen.
     * @param configPath Der Pfad aus dem Config-Objekt (z.B. "/HTTP/PORT" oder "/DOMAIN/NAME").
     * @param registryKeyPath [out] Der Pfad für RegCreateKeyEx relativ zum Hauptschlüssel (z.B. "HTTP" oder "DOMAIN").
     * @param valueName [out] Der Name für RegSetValueExA (z.B. "PORT" oder "NAME").
     */
    void splitConfigPath(const std::string& configPath, std::string& registryKeyPath, std::string& valueName) {
        // Entferne den führenden '/'
        std::string path = configPath.length() > 0 && configPath[0] == '/' ? configPath.substr(1) : configPath;

        // Finde den letzten '/'
        size_t last_slash = path.rfind('/');

        if (last_slash == std::string::npos) {
            // Fall 1: Pfad ist nur der Wertname (z.B. "TEMPLATE")
            registryKeyPath = ""; 
            valueName = path;
        } else {
            // Fall 2: Pfad enthält Subkeys (z.B. "HTTP/PORT")
            // Registry Key Path ist alles vor dem letzten Slash
            registryKeyPath = path.substr(0, last_slash);
            // Value Name ist alles nach dem letzten Slash
            valueName = path.substr(last_slash + 1);

            // Ersetze '/' durch '\' für die Registry-API
            std::replace(registryKeyPath.begin(), registryKeyPath.end(), '/', '\\');
        }
    }


    /**
     * @brief Speichert die Konfiguration in der Windows Registry.
     */
    void Registry::saveConfig(const char *path, const Config *conf){
        HKEY hKey;
        
        // Öffne/Erstelle den Hauptschlüssel (z.B. "SOFTWARE\Blogi")
        HKEY hRootKey;
        DWORD dwDisposition;

        // Der 'path' ist der Basispfad (z.B. "SOFTWARE\Blogi")
        LONG lRes = RegCreateKeyExA(
            HKEY_CURRENT_USER,      
            path,     
            REGISTRY_ROOT_KEY,      
            path,     
            0, NULL, REG_OPTION_NON_VOLATILE, 
            KEY_ALL_ACCESS,         
            NULL, &hKey, &dwDisposition
            KEY_ALL_ACCESS,         
            NULL, &hRootKey, &dwDisposition
        );

        if (lRes != ERROR_SUCCESS) {
            ConfException e;
            throw e[ConfException::Error]<< "Failed to create/open registry key.";
            throw e[ConfException::Error] << "Failed to create/open root registry key: " << path;
        }

        // TODO: Hier die Iterationslogik über conf->getAllKeys() und RegSetValueExA 
        // implementieren, um alle Schlüssel/Werte zu speichern.
        // --- TODO: Hier die Iterationslogik über conf->getAllKeys() implementieren ---
        /*
        for (const auto& key : conf->getAllKeys()) {
            std::string configPath = key->getPath(); // z.B. "/HTTP/PORT"
            std::string cvalue = conf->getValue(key, 0); // Hole den String-Wert
            
        RegCloseKey(hKey);
            std::string subKeyPath;
            std::string valueName;
            splitConfigPath(configPath, subKeyPath, valueName);

            // Temporäres Handle für den Subkey (z.B. "HTTP")
            HKEY hSubKey;
            
            // Erstellt/Öffnet den Subkey relativ zum Root-Key
            lRes = RegCreateKeyExA(hRootKey, subKeyPath.c_str(), 0, NULL, 
                                    REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hSubKey, NULL);

            if (lRes == ERROR_SUCCESS) {
                // Konvertiere den Wert (Hier ist eine einfache String-Speicherung)
                // Die Unterscheidung zwischen REG_SZ und REG_DWORD müsste basierend auf dem
                // tatsächlichen Typ des Werts im Config-Objekt erfolgen.
                
                // Beispiel: Speichere alles als String (REG_SZ)
                lRes = RegSetValueExA(hSubKey, valueName.c_str(), 0, REG_SZ, 
                                      (const BYTE*)cvalue.c_str(), cvalue.length() + 1);

                RegCloseKey(hSubKey);
            } else {
                std::cerr << "Failed to create subkey: " << subKeyPath << std::endl;
            }
        }
        */
        // --- ENDE TODO ---

        RegCloseKey(hRootKey);
    }

    /**
@@ -90,8 +302,9 @@ namespace confplus {
    void Registry::loadConfig(const char* path, Config* conf) {
        HKEY hKey;

        // Öffne den Hauptschlüssel (z.B. "SOFTWARE\Blogi")
        LONG lRes = RegOpenKeyExA(
            HKEY_CURRENT_USER, 
            REGISTRY_ROOT_KEY, // Angenommen HKEY_CURRENT_USER oder HKEY_LOCAL_MACHINE
            path, 
            0, 
            KEY_READ, 
@@ -103,62 +316,9 @@ namespace confplus {
            return;
        }

        DWORD dwValues, dwMaxValueNameLen, dwMaxValueLen;

        lRes = RegQueryInfoKey(
            hKey, NULL, NULL, NULL, NULL, NULL, NULL, 
            &dwValues, &dwMaxValueNameLen, &dwMaxValueLen, 
            NULL, NULL
        );
        // Starte die rekursive Ladefunktion. Startpfad im Config-Objekt ist "" (wird zu /)
        loadKeyRecursiveA(conf, hKey, "");
        
        if (lRes != ERROR_SUCCESS) {
            RegCloseKey(hKey);
            ConfException e;
            throw e[ConfException::Error] << "Failed to query registry key info.";;
        }

        // Buffer allozieren (+1 für Nullterminierung)
        char* valueNameBuffer = new char[dwMaxValueNameLen + 1];
        char* dataBuffer = new char[dwMaxValueLen + 1]; 
        
        DWORD dwSizeValueName, dwSizeData;

        // Iteration über alle Values unter dem Basis-Schlüssel
        for (DWORD i = 0; i < dwValues; ++i) {
            DWORD dwType = 0;
            dwSizeValueName = dwMaxValueNameLen + 1; 
            dwSizeData = dwMaxValueLen + 1;

            lRes = RegEnumValueA(
                hKey, i, 
                valueNameBuffer, &dwSizeValueName, 
                NULL, &dwType, 
                (LPBYTE)dataBuffer, &dwSizeData
            );

            if (lRes == ERROR_SUCCESS) {
                std::string cname = "/";
                cname += std::string(valueNameBuffer); // Konstruiert den Pfad: "/Server/Port"
                std::string cvalue = std::string(dataBuffer);

                if (dwType == REG_SZ) {
                    Config::ConfigData* ckey = conf->setKey(cname);
                    conf->setValue(ckey, 0, cvalue);
                } else if (dwType == REG_DWORD) {
                    std::stringstream ss;
                    ss << *((DWORD*)dataBuffer);
                    Config::ConfigData* ckey = conf->setKey(cname);
                    conf->setValue(ckey, 0, ss.str());
                }
            } else if (lRes == ERROR_NO_MORE_ITEMS) {
                break; 
            } else {
                std::cerr << "Error reading registry value at index " << i << std::endl;
            }
        }

        delete[] valueNameBuffer;
        delete[] dataBuffer;
        RegCloseKey(hKey);
    }
}