summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt18
-rw-r--r--src/config/config.cpp129
-rw-r--r--src/config/config.hpp111
-rw-r--r--src/main.cpp27
-rw-r--r--src/test.cpp26
5 files changed, 304 insertions, 7 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a7ff1d4..b0fae9e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,6 +33,14 @@ add_library(utils STATIC ${source_utils})
target_link_libraries(utils ${ICONV_LIBRARIES})
#
+## config
+#
+file(GLOB source_config
+ src/config/*.[hc]pp)
+add_library(config STATIC ${source_config})
+target_link_libraries(config utils)
+
+#
## network
#
file(GLOB source_network
@@ -64,11 +72,16 @@ file(GLOB source_bridge
add_library(bridge STATIC ${source_bridge})
target_link_libraries(bridge xmpp irc)
+#
+## Main executable
+#
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME}
xmpp
irc
- bridge)
+ bridge
+ utils
+ config)
#
## Tests
@@ -79,6 +92,7 @@ target_link_libraries(test
xmpp
irc
bridge
- utils)
+ utils
+ config)
CONFIGURE_FILE(config.h.cmake src/config.h @ONLY)
diff --git a/src/config/config.cpp b/src/config/config.cpp
new file mode 100644
index 0000000..cad8216
--- /dev/null
+++ b/src/config/config.cpp
@@ -0,0 +1,129 @@
+#include <utils/make_unique.hpp>
+#include <config/config.hpp>
+
+#include <iostream>
+#include <sstream>
+
+std::string Config::filename = "./biboumi.cfg";
+bool Config::file_must_exist = false;
+
+std::string Config::get(const std::string& option, const std::string& def)
+{
+ Config* self = Config::instance().get();
+ auto it = self->values.find(option);
+
+ if (it == self->values.end())
+ return def;
+ return it->second;
+}
+
+int Config::get_int(const std::string& option, const int& def)
+{
+ Config* self = Config::instance().get();
+ std::string res = self->get(option, "");
+ if (!res.empty())
+ return atoi(res.c_str());
+ else
+ return def;
+}
+
+void Config::set_int(const std::string& option, const int& value, bool save)
+{
+ std::ostringstream os;
+ os << value;
+ Config::set(option, os.str(), save);
+}
+
+void Config::set(const std::string& option, const std::string& value, bool save)
+{
+ Config* self = Config::instance().get();
+ self->values[option] = value;
+ if (save)
+ {
+ self->save_to_file();
+ self->trigger_configuration_change();
+ }
+}
+
+void Config::connect(t_config_changed_callback callback)
+{
+ Config* self = Config::instance().get();
+ self->callbacks.push_back(callback);
+}
+
+void Config::close()
+{
+ Config* self = Config::instance().get();
+ self->save_to_file();
+ self->values.clear();
+ Config::instance().reset();
+}
+
+/**
+ * Private methods
+ */
+
+void Config::trigger_configuration_change()
+{
+ std::vector<t_config_changed_callback>::iterator it;
+ for (it = this->callbacks.begin(); it < this->callbacks.end(); ++it)
+ (*it)();
+}
+
+std::unique_ptr<Config>& Config::instance()
+{
+ static std::unique_ptr<Config> instance;
+
+ if (!instance)
+ {
+ instance = std::make_unique<Config>();
+ instance->read_conf();
+ }
+ return instance;
+}
+
+bool Config::read_conf()
+{
+ std::ifstream file;
+ file.open(filename.data());
+ if (!file.is_open())
+ {
+ if (Config::file_must_exist)
+ {
+ perror(("Error while opening file " + filename + " for reading.").c_str());
+ file.exceptions(std::ifstream::failbit);
+ }
+ return false;
+ }
+
+ std::string line;
+ size_t pos;
+ std::string option;
+ std::string value;
+ while (file.good())
+ {
+ std::getline(file, line);
+ if (line == "" || line[0] == '#')
+ continue ;
+ pos = line.find('=');
+ if (pos == std::string::npos)
+ continue ;
+ option = line.substr(0, pos);
+ value = line.substr(pos+1);
+ this->values[option] = value;
+ }
+ return true;
+}
+
+void Config::save_to_file() const
+{
+ std::ofstream file(this->filename.data());
+ if (file.fail())
+ {
+ std::cerr << "Could not save config file." << std::endl;
+ return ;
+ }
+ for (auto& it: this->values)
+ file << it.first << "=" << it.second << std::endl;
+ file.close();
+}
diff --git a/src/config/config.hpp b/src/config/config.hpp
new file mode 100644
index 0000000..ea28388
--- /dev/null
+++ b/src/config/config.hpp
@@ -0,0 +1,111 @@
+/**
+ * Read the config file and save all the values in a map.
+ * Also, a singleton.
+ *
+ * Use Config::filename = "bla" to set the filename you want to use.
+ *
+ * If you want to exit if the file does not exist when it is open for
+ * reading, set Config::file_must_exist = true.
+ *
+ * Config::get() can the be used to access the values in the conf.
+ *
+ * Use Config::close() when you're done getting/setting value. This will
+ * save the config into the file.
+ */
+
+#ifndef CONFIG_INCLUDED
+# define CONFIG_INCLUDED
+
+#include <functional>
+#include <fstream>
+#include <memory>
+#include <vector>
+#include <string>
+#include <map>
+
+typedef std::function<void()> t_config_changed_callback;
+
+class Config
+{
+public:
+ Config(){};
+ ~Config(){};
+ /**
+ * returns a value from the config. If it doesn’t exist, use
+ * the second argument as the default.
+ * @param option The option we want
+ * @param def The default value in case the option does not exist
+ */
+ static std::string get(const std::string&, const std::string&);
+ /**
+ * returns a value from the config. If it doesn’t exist, use
+ * the second argument as the default.
+ * @param option The option we want
+ * @param def The default value in case the option does not exist
+ */
+ static int get_int(const std::string&, const int&);
+ /**
+ * Set a value for the given option. And write all the config
+ * in the file from which it was read if boolean is set.
+ * @param option The option to set
+ * @param value The value to use
+ * @param save if true, save the config file
+ */
+ static void set(const std::string&, const std::string&, bool save = false);
+ /**
+ * Set a value for the given option. And write all the config
+ * in the file from which it was read if boolean is set.
+ * @param option The option to set
+ * @param value The value to use
+ * @param save if true, save the config file
+ */
+ static void set_int(const std::string&, const int&, bool save = false);
+ /**
+ * Adds a function to a list. This function will be called whenever a
+ * configuration change occurs.
+ */
+ static void connect(t_config_changed_callback);
+ /**
+ * Close the config file, saving it to the file is save == true.
+ */
+ static void close();
+
+ /**
+ * Set the value of the filename to use, before calling any method.
+ */
+ static std::string filename;
+ /**
+ * Set to true if you want an exception to be raised if the file does not
+ * exist when reading it.
+ */
+ static bool file_must_exist;
+
+private:
+ /**
+ * Get the singleton instance
+ */
+ static std::unique_ptr<Config>& instance();
+ /**
+ * Read the configuration file at the given path.
+ */
+ bool read_conf();
+ /**
+ * Write all the config values into the configuration file
+ */
+ void save_to_file() const;
+ /**
+ * Call all the callbacks previously registered using connect().
+ * This is used to notify any class that a configuration change occured.
+ */
+ void trigger_configuration_change();
+
+ std::map<std::string, std::string> values;
+ std::vector<t_config_changed_callback> callbacks;
+
+ Config(const Config&) = delete;
+ Config& operator=(const Config&) = delete;
+ Config(Config&&) = delete;
+ Config& operator=(Config&&) = delete;
+};
+
+#endif // CONFIG_INCLUDED
diff --git a/src/main.cpp b/src/main.cpp
index b7fa01e..80c8436 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,13 +1,32 @@
-#include <network/poller.hpp>
#include <xmpp/xmpp_component.hpp>
+#include <network/poller.hpp>
+#include <config/config.hpp>
+#include <iostream>
#include <memory>
-int main()
+int main(int ac, char** av)
{
- Poller p;
+ if (ac > 1)
+ Config::filename = av[1];
+ Config::file_must_exist = true;
+
+ std::string password;
+ try { // The file must exist
+ password = Config::get("password", "");
+ }
+ catch (const std::ios::failure& e) {
+ return 1;
+ }
+ if (password.empty())
+ {
+ std::cerr << "No password provided." << std::endl;
+ return 1;
+ }
std::shared_ptr<XmppComponent> xmpp_component =
- std::make_shared<XmppComponent>("irc.localhost", "secret");
+ std::make_shared<XmppComponent>("irc.abricot", password);
+
+ Poller p;
p.add_socket_handler(xmpp_component);
xmpp_component->start();
while (p.poll())
diff --git a/src/test.cpp b/src/test.cpp
index 674be98..99454a5 100644
--- a/src/test.cpp
+++ b/src/test.cpp
@@ -10,6 +10,8 @@
#include <utils/encoding.hpp>
#include <string.h>
+#include <config/config.hpp>
+
#include <xmpp/xmpp_parser.hpp>
int main()
@@ -49,7 +51,7 @@ int main()
* XML parsing
*/
XmppParser xml;
- const std::string doc = "<stream xmlns='stream_ns'><stanza b='c'>inner<child1/><child2 xmlns='child2_ns'/>tail</stanza></stream>";
+ const std::string doc = "<stream xmlns='stream_ns'><stanza b='c'>inner<child1><grandchild/></child1><child2 xmlns='child2_ns'/>tail</stanza></stream>";
xml.add_stanza_callback([](const Stanza& stanza)
{
assert(stanza.get_name() == "stream_ns:stanza");
@@ -62,5 +64,27 @@ int main()
assert(stanza.get_child("child2_ns:child2")->get_tail() == "tail");
});
xml.feed(doc.data(), doc.size(), true);
+
+ /**
+ * Config
+ */
+ Config::filename = "test.cfg";
+ Config::file_must_exist = false;
+ Config::set("coucou", "bonjour");
+ Config::close();
+
+ bool error = false;
+ try
+ {
+ Config::file_must_exist = true;
+ assert(Config::get("coucou", "") == "bonjour");
+ assert(Config::get("does not exist", "default") == "default");
+ Config::close();
+ }
+ catch (const std::ios::failure& e)
+ {
+ error = true;
+ }
+ assert(error == false);
return 0;
}