Commit 61424840 authored by jan.koester's avatar jan.koester
Browse files

inital support regedit

parent 2edc30cf
Loading
Loading
Loading
Loading
+177 −138
Original line number Diff line number Diff line
@@ -32,61 +32,57 @@
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm> // Für std::replace
#include <stdexcept>
#include <memory> // Für std::unique_ptr

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

namespace confplus {

/*******************************************************************************
 * 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.
 *******************************************************************************/
    // Konfigurierbare Basis-HKEY.
    HKEY REGISTRY_ROOT_KEY = HKEY_CURRENT_USER;
    
#include "regedit.h"
#include "exception.h" // Angenommen: Hier ist ConfException definiert
    // -------------------------------------------------------------------------
    // HILFSFUNKTIONEN
    // -------------------------------------------------------------------------

#include <iostream>
#include <string>
#include <sstream>
#include <stdexcept>
#include <algorithm>
    /**
     * @brief Splittet den Config-Pfad (/Abschnitt/Key) in den Registry Subkey-Pfad (Abschnitt\Key)
     * und den letzten Teil als Value-Namen.
     */
    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;

// Spezifische Windows API Includes
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winreg.h>
        // Finde den letzten '/'
        size_t last_slash = path.rfind('/');

        if (last_slash == std::string::npos) {
            registryKeyPath = ""; 
            valueName = path;
        } else {
            registryKeyPath = path.substr(0, last_slash);
            valueName = path.substr(last_slash + 1);

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

    bool isInteger(const std::string& s) {
        if (s.empty()) return false;
        std::string::const_iterator it = s.begin();
        if (*it == '-' || *it == '+') it++;
        while (it != s.end() && std::isdigit(*it)) ++it;
        return it == s.end();
    }
    
namespace confplus {   
    /**
     * @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) {
        
@@ -101,77 +97,86 @@ namespace confplus {
        );

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

            // Verwendung von std::unique_ptr für automatische Speicherverwaltung
            std::unique_ptr<char[]> valueNameBuffer(new char[maxNameSize]);
            std::unique_ptr<char[]> dataBuffer(new char[maxDataSize]); 
            DWORD dwSizeValueName, dwSizeData, dwType;

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

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

                if (lRes == ERROR_SUCCESS) {
                    // Konstruiere den vollen Konfigurationspfad: /AktuellerPfad/ValueName
                    std::string cname = currentConfigPath + "/" + std::string(valueNameBuffer);
                    std::string valueNameStr = std::string(valueNameBuffer.get());
                    // Überspringe den '(Standardwert)', wenn benannte Werte vorhanden sind
                    if (valueNameStr.empty() && dwValues > 1) {
                        continue;
                    }

                    std::string cname = currentConfigPath + (valueNameStr.empty() ? "" : "/" + valueNameStr);
                    std::string cvalue;

                    if (dwType == REG_SZ) {
                        cvalue = std::string(dataBuffer);
                        cvalue = std::string(dataBuffer.get());
                    } else if (dwType == REG_DWORD) {
                        std::stringstream ss;
                        // Daten sind little-endian in dataBuffer
                        ss << *((DWORD*)dataBuffer);
                        if (dwSizeData >= sizeof(DWORD)) {
                            ss << *((DWORD*)dataBuffer.get());
                            cvalue = ss.str();
                        } else {
                            continue;
                        }
                    } 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) ---
        // --- 3. Subkeys (Unterschlüssel) rekursiv laden ---
        if (dwSubKeys > 0) {
            char* subKeyNameBuffer = new char[dwMaxSubKeyLen + 1];
            DWORD maxSubKeySize = dwMaxSubKeyLen > 0 ? dwMaxSubKeyLen + 1 : 1;
            std::unique_ptr<char[]> subKeyNameBuffer(new char[maxSubKeySize]);
            HKEY hSubKey;

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

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

                    if (lRes == ERROR_SUCCESS) {
                        // Erzeuge den neuen Konfigurationspfad
                        std::string nextConfigPath = currentConfigPath + "/" + std::string(subKeyNameBuffer);
                        std::string nextConfigPath = currentConfigPath + "/" + std::string(subKeyNameBuffer.get());
                        
                        // Rekursiver Aufruf
                        loadKeyRecursiveA(conf, hSubKey, nextConfigPath);
@@ -180,7 +185,6 @@ namespace confplus {
                    }
                }
            }
            delete[] subKeyNameBuffer;
        }
    }

@@ -204,53 +208,117 @@ 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").
     * @brief Traversiert die ConfigData-Struktur rekursiv und speichert die Werte.
     */
    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;
    void Registry::saveKeyRecursive(HKEY hRootKey, std::string currentPath,const Config::ConfigData* currentData, const Config *conf) {
        
        // Finde den letzten '/'
        size_t last_slash = path.rfind('/');
        // Iteriere über die 'nextData'-Kette auf der aktuellen Hierarchie-Ebene
        for (const Config::ConfigData* key = currentData; key != nullptr; key = key->next()) {
            
        if (last_slash == std::string::npos) {
            // Fall 1: Pfad ist nur der Wertname (z.B. "TEMPLATE")
            registryKeyPath = ""; 
            valueName = path;
            // ANNAHME: key->Key enthält den lokalen Schlüsselnamen (z.B. "Datenbank", "Port")
            std::string localKeyName = key->getKey();
            
            if (localKeyName.empty() && !key->childs()) {
                continue;
            }

            std::string subKeyPath = currentPath;
            std::string valueName = localKeyName;
            
            if (key->childs()) {
                // Sektion/Subkey
                if (!subKeyPath.empty()) {
                    subKeyPath += "\\";
                }
                subKeyPath += localKeyName;
            } 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);
                // Wert
                if (!subKeyPath.empty()) {
                    subKeyPath += "\\";
                }
                subKeyPath += localKeyName;

            // Ersetze '/' durch '\' für die Registry-API
            std::replace(registryKeyPath.begin(), registryKeyPath.end(), '/', '\\');
                std::string tempPath;
                splitConfigPath(subKeyPath, tempPath, valueName);
                subKeyPath = tempPath;
            }
            
            HKEY hSubKey;
            LONG lRes = RegCreateKeyExA(hRootKey, subKeyPath.c_str(), 0, NULL, 
                                        REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hSubKey, NULL);
            
            if (lRes == ERROR_SUCCESS) {
                
                size_t elements = key->getElements();
                
                for (size_t pos = 0; pos < elements; ++pos) {
                    
                    std::string cvalue = conf->getValue(key, pos);
                    DWORD dwType = REG_SZ;
                    std::string currentRegValueName = valueName;
                    
                    if (elements > 1) {
                        currentRegValueName = (valueName.empty() ? "" : valueName + "_") + std::to_string(pos);
                    } else if (valueName.empty()) {
                        currentRegValueName = ""; // (Standardwert)
                    }

                    if (isInteger(cvalue)) {
                        dwType = REG_DWORD;
                        try {
                            DWORD dwValue = std::stoul(cvalue); 
                            lRes = RegSetValueExA(hSubKey, currentRegValueName.c_str(), 0, dwType, 
                                                  (const BYTE*)&dwValue, sizeof(dwValue));
                        } catch (const std::exception&) {
                            dwType = REG_SZ;
                            lRes = RegSetValueExA(hSubKey, currentRegValueName.c_str(), 0, dwType, 
                                                  (const BYTE*)cvalue.c_str(), cvalue.length() + 1);
                        }
                    } else {
                        lRes = RegSetValueExA(hSubKey, currentRegValueName.c_str(), 0, dwType, 
                                              (const BYTE*)cvalue.c_str(), cvalue.length() + 1);
                    }

                    if (lRes != ERROR_SUCCESS) {
                        std::cerr << "Failed to set registry value for: " << subKeyPath << "\\" << currentRegValueName << std::endl;
                    }
                }

                // Schließe den Schlüssel, wenn es nur ein Wert war
                if (!key->childs() ) {
                    RegCloseKey(hSubKey);
                }
            } else {
                std::cerr << "Failed to create subkey: " << subKeyPath << std::endl;
            }
            
            // Rekursiver Aufruf für Child-Knoten
            if (key->childs()) { 
                std::string nextPath = subKeyPath;
                
                if (lRes == ERROR_SUCCESS) {
                    saveKeyRecursive(hRootKey, nextPath, key->getChild(), conf);
                    RegCloseKey(hSubKey); // Schließe nach der Rekursion
                }
            }
        }
    }
    
    /**
     * @brief Speichert die Konfiguration in der Windows Registry.
     */
    void Registry::saveConfig(const char *path, const Config *conf){
        
        // Öffne/Erstelle den Hauptschlüssel (z.B. "SOFTWARE\Blogi")
        HKEY hRootKey;
        DWORD dwDisposition;

        // Der 'path' ist der Basispfad (z.B. "SOFTWARE\Blogi")
        // Öffne/Erstelle den Hauptschlüssel
        LONG lRes = RegCreateKeyExA(
            REGISTRY_ROOT_KEY,      
            path,     
            REGISTRY_ROOT_KEY,
            path,
            0, NULL, REG_OPTION_NON_VOLATILE,
            KEY_ALL_ACCESS,         
            KEY_ALL_ACCESS,
            NULL, &hRootKey, &dwDisposition
        );

@@ -259,39 +327,10 @@ namespace confplus {
            throw e[ConfException::Error] << "Failed to create/open root registry key: " << path;
        }

        // --- 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
            
            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;
        // Starte die rekursive Traversierung
        if (conf->firstData) {
            saveKeyRecursive(hRootKey, "", conf->first(), conf);
        }
        }
        */
        // --- ENDE TODO ---
        
        RegCloseKey(hRootKey);
    }
@@ -302,9 +341,9 @@ namespace confplus {
    void Registry::loadConfig(const char* path, Config* conf) {
        HKEY hKey;

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

        // Starte die rekursive Ladefunktion. Startpfad im Config-Objekt ist "" (wird zu /)
        // Starte die rekursive Ladefunktion.
        loadKeyRecursiveA(conf, hKey, "");
        
        RegCloseKey(hKey);
+12 −5
Original line number Diff line number Diff line
@@ -30,6 +30,10 @@
#include "conf.h"
#include "backend.h"

// Wird für HKEY benötigt, um die private Methode zu deklarieren
#define WIN32_LEAN_AND_MEAN
#include <windows.h> 

namespace confplus {
    class Config; // Forward-Deklaration

@@ -49,6 +53,9 @@ namespace confplus {

        Registry();
        virtual ~Registry();
    private:
        // Rekursive Hilfsfunktion zur Speicherung der Config-Datenstruktur
        void saveKeyRecursive(HKEY hRootKey, std::string currentPath,const Config::ConfigData* currentData, const Config *conf);
    };
}

+21 −1
Original line number Diff line number Diff line
@@ -157,6 +157,22 @@ confplus::Config::ConfigData::~ConfigData(){

}

const confplus::Config::ConfigData* confplus::Config::ConfigData::next() const{
    return nextData.get();
}

const std::string& confplus::Config::ConfigData::getKey() const{
    return Key;
}

const bool confplus::Config::ConfigData::childs() const{
    return haveChild;
}

const confplus::Config::ConfigData* confplus::Config::ConfigData::getChild() const{
    return Child.get();
}

size_t confplus::Config::ConfigData::getElements() const{
    return Elements;
}
@@ -295,6 +311,10 @@ const char* confplus::Config::getAuthor(){
    return _currApi->getAuthor();
}

const confplus::Config::ConfigData* confplus::Config::first() const{
    return firstData.get();
}

void confplus::Config::saveConfig(const char *path){
    _currApi->saveConfig(path,this);
}
@@ -306,7 +326,7 @@ size_t confplus::Config::getElements(confplus::Config::ConfigData* key) const{
    return key->Elements;
}

const std::string &confplus::Config::getValue(confplus::Config::ConfigData* key, size_t pos) const{
const std::string &confplus::Config::getValue(const confplus::Config::ConfigData* key, size_t pos) const{
    if (!key) {
        ConfException err;
        err[ConfException::Error] << "Key not found" << pos;
+9 −2
Original line number Diff line number Diff line
@@ -61,9 +61,14 @@ namespace confplus {
        public:
            ~ConfigData();
            ConfigData();
        private:
            const ConfigData *next() const;
            const std::string &getKey() const;

            const bool childs() const;
            const ConfigData *getChild() const;
            size_t getElements() const;

        private:
            std::string  Key;
            size_t       Elements;

@@ -90,7 +95,7 @@ namespace confplus {
        void setValue(ConfigData *key,size_t pos,const std::string &value);
        void setIntValue(ConfigData *key,size_t pos,int value);

        const std::string &getValue(ConfigData *key,size_t pos) const;
        const std::string &getValue(const ConfigData *key,size_t pos) const;
        int  getIntValue(ConfigData *key,size_t pos) const;

        std::unique_ptr<ConfigData>firstData=nullptr;
@@ -99,6 +104,8 @@ namespace confplus {
        const char* getVersion();
        const char* getAuthor();

        const ConfigData* first() const;

        void saveConfig(const char *path);
    private:
        BackendApi *_currApi;