diff options
Diffstat (limited to 'libwes/src/cfg.c.draft')
-rw-r--r-- | libwes/src/cfg.c.draft | 707 |
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); +} |