aboutsummaryrefslogtreecommitdiff
path: root/libwes/src/cfg.c.draft
diff options
context:
space:
mode:
Diffstat (limited to 'libwes/src/cfg.c.draft')
-rw-r--r--libwes/src/cfg.c.draft707
1 files changed, 707 insertions, 0 deletions
diff --git a/libwes/src/cfg.c.draft b/libwes/src/cfg.c.draft
new file mode 100644
index 0000000..189c9c6
--- /dev/null
+++ b/libwes/src/cfg.c.draft
@@ -0,0 +1,707 @@
+/* ****************************************************************************
+ * cfg.c -- gestion de la configuration par FTP des serveurs WES.
+ * Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
+ *
+ * This project is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software. You can use,
+ * modify and/or redistribute the software under the terms of the
+ * CeCILL license as circulated by CEA, CNRS and INRIA at the
+ * following URL: "http://www.cecill.info".
+ *
+ * As a counterpart to the access to the source code and rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty and the project's author, the holder of the
+ * economic rights, and the successive licensors have only limited liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading, using, modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean that it is complicated to manipulate, and that also therefore
+ * means that it is reserved for developers and experienced professionals
+ * having in-depth computer knowledge. Users are therefore encouraged to load
+ * and test the software's suitability as regards their requirements in
+ * conditions enabling the security of their systems and/or data to be
+ * ensured and, more generally, to use and operate it in the same conditions
+ * as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms.
+ * ************************************************************************* */
+#include "internals.h"
+
+/* FIXME: tout ça ne gère pas la partie "données" de certains fichiers
+ * de configuration gérées par le WES, dont notamment `S1WIRE.CFG`.
+ * Il s'agit de les inclure aussi un de ces jours. */
+
+#define SECSIZE 10
+#define KEYSIZE 50
+#define VALSIZE 100
+
+#define DEFAULTSECTION "RESEAU"
+
+#define validkeycar(CAR) (isalnum(CAR))
+#define validvalcar(CAR) (isalnum(CAR) || strchr(".- ", (CAR)))
+
+/* ---
+ * Utilitaires.
+ * --- */
+
+/* `get_request()`: Préparation d'une requête, avec URL et identifiants. */
+
+static int get_request(wes_t *wes, CURL **handlep, const char *section)
+{
+ char path[10 + SECSIZE];
+
+ sprintf(path, "/CFG/%s.CFG", section);
+ return (open_ftp_handle(wes, handlep, path));
+}
+
+/* `normalize_section()`: Normalisation du nom de section. */
+
+static int normalize_section(char *buf, const char *src)
+{
+ int left, car;
+
+ for (left = SECSIZE; left && *src; src++) {
+ car = *src;
+ if (car == '.')
+ break;
+ if (!isalnum(car))
+ continue;
+
+ left--;
+ *buf++ = (char)car;
+ if (!left)
+ break;
+ }
+ *buf = 0;
+
+ /* Si la forme normalisée est vide, on utilise la section par
+ * défaut, ici « RESEAU ». */
+
+ if (left == SECSIZE)
+ memcpy(buf, DEFAULTSECTION, sizeof(DEFAULTSECTION));
+
+ return (WROK);
+}
+
+/* `normalize_key()`: Normalisation du nom de clé. */
+
+static int normalize_key(char *buf, const char *src)
+{
+ int left, car;
+
+ for (left = KEYSIZE; left && *src; src++) {
+ car = *src;
+ if (!validkeycar(car))
+ continue;
+
+ left--;
+ *buf++ = (char)car;
+ if (!left)
+ break;
+ }
+ *buf = 0;
+
+ if (left == KEYSIZE)
+ return (WROP);
+ return (WROK);
+}
+
+/* `normalize_value()`: Normalisation de la valeur. */
+
+static int normalize_value(char *buf, const char *src)
+{
+ int left, car;
+
+ for (left = VALSIZE; left && *src; src++) {
+ car = *src;
+ if (!validvalcar(car))
+ continue;
+
+ left--;
+ *buf++ = (char)car;
+ if (!left)
+ break;
+ }
+ *buf = 0;
+
+ return (WROK);
+}
+
+/* `emptysection()`: Vider une section de configuration. */
+
+static int emptysection(wescfgsec_t *sec)
+{
+ wescfgkey_t *cfg;
+
+ while ((cfg = sec->child)) {
+ sec->child = cfg->next;
+ free(cfg);
+ }
+
+ return (WROK);
+}
+
+/* `setsectionkeyval()`: Définition de la valeur pour une clé de la
+ * section. */
+
+static int setsectionkeyval(wescfgsec_t *sec, const char *key, const char *val)
+{
+ wescfgkey_t **cfgp, *cfg;
+ const char *temp;
+ size_t keysz, valsz;
+
+ temp = memchr(key, 0, KEYSIZE);
+ keysz = temp ? (size_t)(temp - key) : KEYSIZE;
+ temp = memchr(val, 0, VALSIZE);
+ valsz = temp ? (size_t)(temp - val) : VALSIZE;
+
+ cfgp = &sec->child;
+ for (cfgp = &sec->child; (cfg = *cfgp); cfgp = &cfg->next) {
+ if (cfg->keysz == keysz && !memcmp(cfg->data, key, keysz)) {
+ if (cfg->valsz == valsz && !memcmp(&cfg->data[keysz], val, valsz))
+ return (WROK);
+
+ *cfgp = cfg->next;
+ free(cfg);
+ break;
+ }
+ }
+
+ cfg = malloc(sizeof(wescfgkey_t) + keysz + valsz);
+ if (!cfg)
+ return (WRALLOC);
+ cfg->next = *cfgp;
+ *cfgp = cfg;
+ cfg->keysz = keysz;
+ cfg->valsz = valsz;
+ memcpy(cfg->data, key, keysz);
+ memcpy(&cfg->data[keysz], val, valsz);
+
+ return (WROK);
+}
+
+/* ---
+ * Lecture de section (récupération d'un fichier via FTP).
+ * --- */
+
+#define SG_KEY 0 /* clé */
+#define SG_VAL 1 /* valeur */
+
+struct getcookie {
+ wescfgsec_t *sec;
+ int err;
+ int step, off, hadval;
+ char keybuf[KEYSIZE + 1];
+ char valbuf[VALSIZE + 1];
+};
+
+/* `getsection_callback()`: Callback de la fonction de récupération de
+ * section. */
+
+static size_t getsection_callback(char *buffer, size_t size, size_t nmemb,
+ struct getcookie *cookie)
+{
+ int car, save, err;
+ size_t memb_size = size, orig_size;
+
+ if (cookie->err != WROK)
+ return (0);
+
+ size = size * nmemb;
+ orig_size = size;
+ for (; size; size--) {
+ car = *buffer++;
+
+ save = 0;
+ switch (cookie->step) {
+ case SG_KEY:
+ switch (car) {
+ case '=':
+ cookie->keybuf[cookie->off] = 0;
+ cookie->off = 0;
+ cookie->step = SG_VAL;
+ cookie->hadval = 1;
+ break;
+ case '\n':
+ cookie->keybuf[cookie->off] = '\0';
+ cookie->valbuf[0] = '\0';
+ save = 1;
+ break;
+ default:
+ if (isalnum(car) && cookie->off < KEYSIZE)
+ cookie->keybuf[cookie->off++] = (char)car;
+ break;
+ }
+ break;
+ case SG_VAL:
+ switch (car) {
+ case '\n':
+ cookie->valbuf[cookie->off] = '\0';
+ save = 1;
+ break;
+ default:
+ if (validvalcar(car) && cookie->off < VALSIZE)
+ cookie->valbuf[cookie->off++] = (char)car;
+ break;
+ }
+ break;
+ }
+
+ if (save) {
+ /* On vérifie si :
+ * - il y a eu un '=' (`hadval == 1`) ;
+ * - la clé et la valeur sont non vides. */
+
+ if (cookie->hadval && cookie->keybuf[0] && cookie->valbuf[0]) {
+ if ((err = setsectionkeyval(cookie->sec, cookie->keybuf,
+ cookie->valbuf))) {
+ cookie->err = err;
+ return ((orig_size - size) / memb_size);
+ }
+ }
+
+ /* Dans tous les cas, on passe au prochain élément de
+ * configuration. */
+
+ cookie->step = SG_KEY;
+ cookie->off = 0;
+ cookie->hadval = 0;
+ }
+ }
+
+ return (nmemb);
+}
+
+/* `gathersection()`: Récupération d'une section de configuration. */
+
+static int gathersection(wes_t *wes, wescfgsec_t *sec)
+{
+ struct getcookie cookie;
+ CURL *curl; CURLcode cres;
+ const char *section;
+ int err;
+
+ section = sec->data;
+
+ /* On vide la section actuelle. */
+
+ emptysection(sec);
+
+ /* On initialise la requête. */
+
+ if ((err = get_request(wes, &curl, section)))
+ return (err);
+
+ cookie.sec = sec;
+ cookie.step = SG_KEY;
+ cookie.off = 0;
+ cookie.hadval = 0;
+ cookie.err = WROK;
+
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, getsection_callback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &cookie);
+
+ cres = curl_easy_perform(curl);
+
+ if (cres != CURLE_OK) {
+ emptysection(sec);
+
+ switch (cres) {
+ case CURLE_OK:
+ /* Tout s'est bien passé. */
+ break;
+
+ case CURLE_READ_ERROR:
+ case CURLE_WRITE_ERROR:
+ /* Le callback d'écriture a fait s'arrêter le transfert. */
+
+ return (cookie.err);
+
+ case CURLE_OUT_OF_MEMORY:
+ /* Une allocation a échoué. */
+
+ return (WRALLOC);
+
+ case CURLE_COULDNT_RESOLVE_HOST: /* FALLTHRU */
+ case CURLE_COULDNT_CONNECT: /* FALLTHRU */
+ case CURLE_FTP_CANT_GET_HOST: /* FALLTHRU */
+ case CURLE_SEND_ERROR: /* FALLTHRU */
+ case CURLE_RECV_ERROR:
+ /* Quelque chose s'est mal passé sur la transmission. */
+
+ return (WRNOHOST);
+
+ case CURLE_REMOTE_FILE_NOT_FOUND:
+ /* Le fichier n'existe pas.
+ * FIXME: Crée-t-on la configuration par défaut ? */
+
+ return (WRUNKNOWN);
+
+ default:
+ return (WRUNKNOWN);
+ }
+ }
+
+ return (WROK);
+}
+
+/* ---
+ * Écriture de section (envoi d'un fichier via FTP).
+ * --- */
+
+#define SC_KEY 0 /* key */
+#define SC_EQU 1 /* '=' */
+#define SC_VAL 2 /* val */
+#define SC_EOL 3 /* <CR><LF> */
+#define SC_NEXT 4
+
+struct sendcookie {
+ wescfgkey_t *cfg;
+ int step, err;
+ size_t off;
+};
+
+/* `sendsection_callback()`: Callback de la fonction d'envoi de section. */
+
+static size_t sendsection_callback(char *buffer, size_t blocksize, size_t size,
+ struct sendcookie *cookie)
+{
+ wescfgkey_t *cfg;
+ const char *tocopy;
+ size_t off, csize, copysize;
+ size_t wr = 0;
+
+ if (cookie->err != WROK)
+ return (0);
+
+ size *= blocksize;
+ while (size) {
+ cfg = cookie->cfg;
+ if (!cfg)
+ break;
+
+ /* Sélection de ce qu'il faut copier. */
+
+ switch (cookie->step) {
+ case SC_KEY:
+ tocopy = cfg->data;
+ csize = cfg->keysz;
+ break;
+ case SC_EQU:
+ tocopy = "=";
+ csize = 1;
+ break;
+ case SC_VAL:
+ tocopy = &cfg->data[cfg->keysz];
+ csize = cfg->valsz;
+ break;
+ case SC_EOL:
+ tocopy = "\r\n";
+ csize = 2;
+ break;
+ default:
+ cookie->err = WRUNKNOWN;
+ return (wr / blocksize);
+ }
+
+ /* Calcul de ce qu'il faut copier, préparation de la prochaine
+ * lecture. */
+
+ copysize = size;
+ off = cookie->off;
+ cookie->off += off;
+ if (cookie->off + copysize >= csize) {
+ copysize = csize - off;
+ cookie->off = 0;
+ cookie->step++;
+ if (cookie->step == SC_NEXT) {
+ cookie->step = SC_KEY;
+ cookie->cfg = cfg->next;
+ }
+ }
+
+ memcpy(buffer, &tocopy[off], copysize);
+ buffer += copysize;
+ size -= copysize;
+ wr += copysize;
+ }
+
+ return (wr / blocksize);
+}
+
+/* `sendsection()`: Envoi d'une section de configuration. */
+
+static int sendsection(wes_t *wes, wescfgsec_t *sec)
+{
+ CURL *curl; CURLcode cres;
+ struct sendcookie cookie;
+ curl_off_t fsize;
+ wescfgkey_t *cfg;
+ int err;
+
+ /* On calcule le nombre total de bytes que cela va prendre. */
+
+ fsize = 0;
+ for (cfg = sec->child; cfg; cfg = cfg->next)
+ fsize += cfg->keysz + cfg->valsz + 3; /* '=', <CR>, <LF> */
+
+ /* On initialise la requête. */
+
+ curl = curl_easy_init();
+ if (!curl)
+ return (WRUNKNOWN);
+
+ if ((err = get_request(wes, &curl, &sec->data[sec->keysz])))
+ return (err);
+
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+ curl_easy_setopt(curl, CURLOPT_READDATA, &cookie);
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, sendsection_callback);
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fsize);
+
+ cres = curl_easy_perform(curl);
+
+ switch (cres) {
+ case CURLE_OK:
+ /* Tout s'est bien passé. */
+ break;
+
+ case CURLE_READ_ERROR:
+ case CURLE_WRITE_ERROR:
+ /* Le callback de lecture a fait s'arrêter le transfert. */
+
+ return (cookie.err);
+
+ case CURLE_OUT_OF_MEMORY:
+ /* Une allocation a échoué. */
+
+ return (WRALLOC);
+
+ case CURLE_COULDNT_RESOLVE_HOST: /* FALLTHRU */
+ case CURLE_COULDNT_CONNECT: /* FALLTHRU */
+ case CURLE_FTP_CANT_GET_HOST: /* FALLTHRU */
+ case CURLE_SEND_ERROR: /* FALLTHRU */
+ case CURLE_RECV_ERROR:
+ /* Quelque chose s'est mal passé sur la transmission. */
+
+ return (WRNOHOST);
+
+ default:
+ return (WRUNKNOWN);
+ }
+
+ return (WROK);
+}
+
+/* ---
+ * Gestion plus haut niveau de la configuration.
+ * --- */
+
+static int getsection(wes_t *wes, wescfgsec_t **secresp, const char *section)
+{
+ wescfgsec_t *sec, **secp;
+ size_t sz; int err;
+
+ /* Recherche de la section. */
+
+ sz = strlen(section);
+ for (secp = &wes->sections; (sec = *secp); secp = &sec->next) {
+ if (sz == sec->keysz && !memcmp(sec->data, section, sz))
+ break;
+ }
+
+ /* Si la section n'a pas été trouvée, on la récupère. */
+
+ if (!sec) {
+ *secp = malloc(sizeof(wescfgsec_t) + sz + 1);
+ sec = *secp;
+ if (!sec)
+ return (WRALLOC);
+
+ sec->next = NULL;
+ sec->child = NULL;
+ sec->keysz = sz;
+ memcpy(sec->data, section, sz);
+ sec->data[sz] = 0;
+
+ err = gathersection(wes, sec);
+ if (err) {
+ free(sec);
+ *secp = NULL;
+ return (err);
+ }
+
+ sec = *secp;
+ }
+
+ *secresp = sec;
+ return (WROK);
+}
+
+static int getsectionkey(wes_t *wes, const char *section, const char *key,
+ wescfgkey_t **cfgp)
+{
+ wescfgsec_t *sec;
+ wescfgkey_t *cfg;
+ size_t keysz;
+ int err;
+
+ /* Récupération de la section. */
+
+ if ((err = getsection(wes, &sec, section)))
+ return (err);
+
+ /* On recherche l'élément de configuration en utilisant la clé. */
+
+ keysz = strlen(key);
+ for (cfg = sec->child; cfg; cfg = cfg->next)
+ if (keysz == cfg->keysz && !memcmp(cfg->data, key, keysz))
+ break;
+ if (!cfg)
+ return (WRNOTFOUND);
+
+ /* L'élément a été trouvé, on a juste à le retourner :) */
+
+ *cfgp = cfg;
+ return (WROK);
+}
+
+static int setsectionkey(wes_t *wes, const char *section, const char *key,
+ const char *value)
+{
+ wescfgsec_t *sec;
+ int err;
+
+ /* Récupération de la section. */
+
+ if ((err = getsection(wes, &sec, section)))
+ return (err);
+
+ /* On inscrit la donnée en utilisant la fonction déjà utilisée
+ * dans `gathersection()`. */
+
+ err = setsectionkeyval(sec, key, value);
+ if (err)
+ return (err);
+
+ /* Si on est là, c'est que la donnée de configuration a été écrite avec
+ * succès ! Bien joué ! */
+
+ return (WROK);
+}
+
+static int delsectionkey(wes_t *wes, const char *section, const char *key)
+{
+ wescfgsec_t *sec;
+ wescfgkey_t **cfgp, *cfg, *next;
+ size_t keysz; int err;
+
+ /* Récupération de la section. */
+
+ if ((err = getsection(wes, &sec, section)))
+ return (err);
+
+ /* On cherche la donnée. */
+
+ keysz = strlen(key);
+ for (cfgp = &sec->child; (cfg = *cfgp); cfgp = &cfg->next) {
+ if (keysz == cfg->keysz && !memcmp(cfg->data, key, keysz))
+ break;
+ }
+ if (!cfg)
+ return (WRNOTFOUND);
+
+ /* On la supprime. */
+
+ next = cfg->next;
+ free(cfg);
+ *cfgp = next;
+
+ /* On a bien supprimé la donnée ! */
+
+ return (WROK);
+}
+
+/* ---
+ * Fonctions pour l'utilisateur concernant la configuration.
+ * --- */
+
+/* `wes_get_config()`: Récupération d'un élément de configuration. */
+
+int wes_get_config(WES *wes, const char *section_src, const char *key_src,
+ char *buf, size_t len)
+{
+ int err; wes_cfg_key_t *cfg;
+ char section[SECSIZE + 1];
+ char key[KEYSIZE + 1];
+
+ if ((err = normalize_section(section, section_src))
+ || (err = normalize_key(key, key_src)))
+ return (err);
+
+ if ((err = getsectionkey(wes, section, key, &cfg)))
+ return (err);
+
+ if (len > cfg->valsz + 1)
+ len = cfg->valsz + 1;
+ len--;
+ memcpy(buf, &cfg->data[cfg->keysz], len);
+ buf[len] = 0;
+
+ return (WROK);
+}
+
+/* `set_config()`: Définition d'un élément de configuration. */
+
+int set_config(WES *wes, const char *section_src, const char *key_src,
+ const char *value_src)
+{
+ int err;
+ char section[SECSIZE + 1];
+ char key[KEYSIZE + 1];
+ char value[VALSIZE + 1];
+
+ if ((err = normalize_section(section, section_src))
+ || (err = normalize_key(key, key_src))
+ || (err = normalize_value(key, value_src)))
+ return (err);
+
+ err = *value ? setsectionkey(wes, section, key, value)
+ : delsectionkey(wes, section, key);
+ if (err)
+ return (err);
+
+ return (WROK);
+}
+
+/* `apply_config()`: Application de la configuration. */
+
+int apply_config(WES *wes)
+{
+ wescfgsec_t *sec;
+ int err;
+
+ for (sec = wes->sections; sec; sec++) {
+ if ((err = sendsection(wes, sec)))
+ return (err);
+ }
+
+ return (WROK);
+}
+
+/* `clear_config()`: Vidage de la configuration. */
+
+int clear_config(WES *wes)
+{
+ wescfgsec_t *sec;
+
+ while ((sec = wes->sections)) {
+ wes->sections = sec->next;
+ emptysection(sec);
+ free(sec);
+ }
+
+ return (WROK);
+}