diff --git a/Cargo.toml b/Cargo.toml
index 6c28926d..b4ce60df 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,8 @@ lazy_static = "1"
enum-set = "0.0"
clap = { version = "3.2.17", features = ["derive"] }
directories = "4.0.1"
+configparser = "3.0.1"
+jid = "0.9.4"
[lib]
crate-type = ["cdylib"]
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 00000000..feaf51ca
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,177 @@
+// Copyright (C) 2018-2099 The crate authors.
+//
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the
+// Free Software Foundation, either version 3 of the License, or (at your
+// option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+use crate::error::Error;
+
+use std::cell::LazyCell;
+use std::collections::HashMap;
+use std::fmt;
+use std::path::PathBuf;
+
+use configparser::ini::Ini;
+use jid::Jid;
+
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+pub(crate) enum ConfigValue {
+ Bool(bool),
+ UInt(u64),
+ Int(i64),
+ String(String),
+}
+
+impl ConfigValue {
+ pub(crate) fn to_bool>(_val: S) -> Result {
+ Ok(ConfigValue::Bool(true))
+ }
+
+ pub(crate) fn to_uint>(_val: S) -> Result {
+ Ok(ConfigValue::UInt(0u64))
+ }
+
+ pub(crate) fn to_int>(_val: S) -> Result {
+ Ok(ConfigValue::Int(0i64))
+ }
+
+ pub(crate) fn to_string>(_val: S) -> Result {
+ Ok(ConfigValue::String(String::new()))
+ }
+}
+
+impl fmt::Display for ConfigValue {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ ConfigValue::Bool(b) => b.to_string(),
+ ConfigValue::UInt(i) => i.to_string(),
+ ConfigValue::Int(i) => i.to_string(),
+ ConfigValue::String(s) => s.to_string(),
+ }
+ )
+ }
+}
+
+const DEFAULT_CONFIG: LazyCell>> =
+ LazyCell::new(|| HashMap::new());
+
+pub(crate) struct Config<'a> {
+ filename: PathBuf,
+ defaults: LazyCell>>,
+ ini: Ini,
+}
+
+impl Config<'static> {
+ /// Create a new Config object
+ pub(crate) fn new>(filename: P) -> Self {
+ // TODO: read ini file.
+
+ Config {
+ filename: filename.into(),
+ defaults: DEFAULT_CONFIG,
+ ini: Ini::new(),
+ }
+ }
+
+ /// Sets a key/value pair in memory and updates the config file.
+ pub(crate) fn set(
+ &mut self,
+ key: &str,
+ value: ConfigValue,
+ section: Option,
+ ) -> Result<(), Error> {
+ let section: String = section
+ .map(|jid| jid.to_string())
+ .unwrap_or(String::from("default"));
+ self.write(key.clone(), value.clone(), section.as_str())?;
+ let _ = self.ini.set(section.as_str(), key, Some(value.to_string()));
+ Ok(())
+ }
+
+ fn write(&self, _key: &str, _value: ConfigValue, _section: &str) -> Result<(), Error> {
+ // TODO: Copy the file and parse it manually. If the key exists, edit it in place during
+ // parsing. If it doesn't, add it at the end of the section. Replace the original file
+ // atomically.
+ Ok(())
+ }
+
+ /// Fetches a value
+ pub(crate) fn get(
+ &self,
+ key: &str,
+ section: Option,
+ ) -> Result